Implementation of Android System Level Suspension Button

Keywords: Android Mobile Windows Attribute

Specific needs
 1. Make a system-level suspension button, just like the effect of the suspension button on the desktop of the iPhone, which can be dragged at will, and the suspension button will automatically pull aside as soon as the hand is released.
2. It can be clicked and dragged at will.
3. When the suspension button comes to the edge automatically, or when it moves to the edge, it hides the half automatically.
4. Both horizontal and vertical screen switching are compatible

1. Add View to Windows Manager, which is implemented by custom controls.
2. In the MotionEvent.ACTION_MOVE event in onTouch, the movement is achieved by controlling the specific coordinates of the suspension button.
3. In the MotionEvent.ACTION_UP event in onTouch, the suspension button is controlled to automatically pull aside and hide half of the button. But here the onTouch and onClick events are triggered together, but there is also a solution. You can decide whether to trigger the click event or not at the moment when your hand is open, by the distance of movement. If you return to false, the trigger point will be triggered. Click event, if true is returned, the click event will be triggered
 4. Capturing horizontal and vertical screen switching events by using the custom control onLayout method.
5. There is also a question of which side to park on, which side to park closer to by coordinates. It's on the right side.
(http://img.blog.csdn.net/20170316151428650? Watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZQvSHVhbmdUaWVCaW5n/font/5a6L5T/fonize/400/fill/I0JBQkFCMA=/dissolve/70/gravity/East)

The following is the specific implementation code:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

import com.iapppay.openid.channel.LoginResultCallback;
import com.iapppay.openid.channel.OpenIDApplication;
import com.iapppay.openid.channel.util.DisplayUtil;
import com.iapppay.openid.channel.util.LogUtil;
import com.iapppay.openid.channel.util.Res;

/**
 * Created by HuangTiebing 2017/2/14.
 */

public class DragFloatActionButton extends ImageView implements View.OnTouchListener, View.OnClickListener {

    public static String TAG = "DragFloatActionButton";
    private Context context;

    float lastX, lastY;
    float originX, originY;
    int screenWidth;
    int screenHeight;
    private int originWidth;

    private WindowManager windowManager;
    //    // This Windows Manager Params variable is the acquired global variable to hold the properties of the suspended window.
    private WindowManager.LayoutParams windowManagerParams;

    private LoginResultCallback resultCallback; //Suspension button click callback

    public DragFloatActionButton(Context context, boolean isForceLogin, LoginResultCallback resultCallback) {
        this(context, null);
        OpenIDApplication.getInstance().setForceLogin(isForceLogin);
        this.resultCallback = resultCallback;
    }

    public DragFloatActionButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;

        Point screenSize = DisplayUtil.getScreenSize(context);
        screenWidth = screenSize.x;
        screenHeight = screenSize.y;
        setImageResource(Res.drawable(context, "ipay_float_btn_bg"));
        setOnTouchListener(this);
        setOnClickListener(this);

        windowManager = (WindowManager) getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
    }

    public int getOriginWidth() {
        return originWidth;
    }

    public void setOriginWidth(int originWidth) {
        this.originWidth = originWidth;
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        windowManagerParams = (WindowManager.LayoutParams) this.getLayoutParams();
        //Get the height of the status bar
        Rect frame = new Rect();
        getWindowVisibleDisplayFrame(frame);
        int ea = event.getAction();
        switch (ea) {
            case MotionEvent.ACTION_DOWN:
                lastX = event.getRawX();// Get the original X coordinates of the touch position of the touch event
                lastY = event.getRawY();
                originX = lastX;
                originY = lastY;
                break;
            case MotionEvent.ACTION_MOVE:
                float dx = event.getRawX() - lastX;
                float dy = event.getRawY() - lastY;
                windowManagerParams.x += dx;
                windowManagerParams.y += dy;
                LogUtil.d(TAG, "Moving distance: dx=" + dx + ",dy=" + dy);
                showAllBtn();
                lastX = (int) event.getRawX();
                lastY = (int) event.getRawY();
                break;
            case MotionEvent.ACTION_UP:
                float lastMoveDx = Math.abs(event.getRawX() - originX);
                float lastMoveDy = Math.abs(event.getRawY() - originY);
                LogUtil.d(TAG, "When loosened, move distance: lastMoveDx=" + lastMoveDx + ", lastMoveDy=" + lastMoveDy);
                if (lastMoveDx < 10 && lastMoveDy < 10) { //If the distance is too small, it will be regarded as a click.
                    return false;
                } else {
                    updateViewLayout(event);
                    isFirstClick = true;
                    return true;
                }
        }
        return false;
    }

    /**
     * Display the entire Icon
     */
    public void showAllBtn() {
        windowManagerParams.width = originWidth;
        windowManagerParams.height = originWidth;
        setImageResource(Res.drawable(context, "ipay_float_btn_bg"));
        windowManager.updateViewLayout(this, windowManagerParams); // Refresh display
    }

    /**
     * The suspension button appears on the left
     */
    private void showInLeft() {
        windowManagerParams.x = 0;
        windowManagerParams.width = originWidth / 2;
        windowManagerParams.height = originWidth;
        setImageResource(Res.drawable(context, "ipay_float_btn_left_hidden"));
        windowManager.updateViewLayout(this, windowManagerParams); // Refresh display
    }

    /**
     * The suspension button is displayed on the right.
     */
    private void showInRight() {
        windowManagerParams.width = originWidth / 2;
        windowManagerParams.height = originWidth;
        windowManagerParams.x = screenWidth - windowManagerParams.width;
        setImageResource(Res.drawable(context, "ipay_float_btn_right_hidden"));
        windowManager.updateViewLayout(this, windowManagerParams); // Refresh display
    }

    /**
     * The suspension button is displayed on it.
     */
    private void showInTop() {
        windowManagerParams.y = 0;
        windowManagerParams.width = originWidth;
        windowManagerParams.height = originWidth / 2;
        setImageResource(Res.drawable(context, "ipay_float_btn_top_hidden"));
        windowManager.updateViewLayout(this, windowManagerParams); // Refresh display
    }

    /**
     * The suspension button is displayed below.
     */
    private void showInBottom() {
        windowManagerParams.width = originWidth;
        windowManagerParams.height = originWidth / 2;
        windowManagerParams.y = screenHeight - windowManagerParams.width;
        setImageResource(Res.drawable(context, "ipay_float_btn_bottom_hidden"));
        windowManager.updateViewLayout(this, windowManagerParams); // Refresh display
    }

    /**
     * Update the suspension Icon
     *
     * @param event Manual Mobile Events
     */
    public void updateViewLayout(MotionEvent event) {
        Point center = new Point(screenWidth / 2, screenHeight / 2); //Screen Center
        float xOffset, yOffset;//The offset on X-axis and Y-axis with the center of the screen as the origin
        if (event != null) {//Manual Mobile
            xOffset = event.getRawX() - center.x;
            yOffset = event.getRawY() - center.y;
        } else {//Automatic hiding
            xOffset = lastX - center.x;
            yOffset = lastY - center.y;
        }
        if (Math.abs(xOffset) >= Math.abs(yOffset)) {//Indent left or right to hide
            if (xOffset <= 0) { //Indent to the left
                showInLeft();
            } else {
                showInRight();
            }
        } else {//Up or down indentation hiding
            if (yOffset <= 0) {//Retract upward
                showInTop();
            } else {
                showInBottom();
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Point screenSize = DisplayUtil.getScreenSize(context);
        if (screenWidth != screenSize.x) {//Screen Rotation Switching
            screenWidth = screenSize.x;
            screenHeight = screenSize.y;
            lastY = windowManagerParams.x;
            lastX = windowManagerParams.y;
            windowManagerParams.x = (int) lastX;
            windowManagerParams.y = (int) lastY;
            updateViewLayout(null);
        }
    }

    private boolean isFirstClick = true;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @Override
    public void onClick(View v) {
        LogUtil.d(TAG, "Execute click events");
        if (!isFirstClick) {
            OpenIDApplication.getInstance().floatBtnClick(context, OpenIDApplication.getInstance().isForceLogin(), resultCallback);
        } else {//Semi-hidden status, Click to display all
            isFirstClick = false;
            showAllBtn();
        }
    }

}

Call the implementation code, note here that there is a problem, pop-up system-level suspension window, need to configure permissions:
And android 6.0 mobile phones, but also pop-up dialog box to ask whether the user is running, if the user refused, it can not pop up the system-level suspension window, there are individual mobile phone manufacturers modified the android source code, but also need to enter the system settings to allow the application to pop up the suspension window. In this way, the experience is very bad, but here's a trick. Setting to toast type as follows completely solves the problem, neither configuring permissions nor popping up windows to obtain permissions from users.

WindowManager.LayoutParams windowManagerParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);

The specific implementation code is as follows:

DragFloatActionButton  floatBtn = new DragFloatActionButton(context, isForceLogin, mResultCallback);

            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            // Setting LayoutParams (Global Variable) Related Parameters
            WindowManager.LayoutParams windowManagerParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.TYPE_TOAST,
                    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT);
            /**
             * Note that the value of flag can be:
             * The effect of the flags attribute below is the same as "lock".
             * The suspension window is untouchable, does not accept any event, and does not affect the subsequent event response.
             * LayoutParams.FLAG_NOT_TOUCH_MODAL It does not affect subsequent events.
             * LayoutParams.FLAG_NOT_FOCUSABLE  Non focusing
             * LayoutParams.FLAG_NOT_TOUCHABLE Impossible to touch
             */
            // Adjust the suspension window to the upper left corner for easy adjustment of coordinates
            windowManagerParams.gravity = Gravity.LEFT | Gravity.TOP;
            // Starting at the top left corner of the screen, setx,yinitial value
            windowManagerParams.x = 0;
            windowManagerParams.y = 0;
            // Setting Length and Width Data of Suspension Window
            floatBtn.measure(0, 0);
            floatBtn.setOriginWidth(floatBtn.getMeasuredWidth() - 50);
            windowManagerParams.width = floatBtn.getOriginWidth();
            windowManagerParams.height = windowManagerParams.width;
            // Display myFloatView image
            windowManager.addView(floatBtn, windowManagerParams);

Posted by praxedis on Sat, 20 Apr 2019 22:00:34 -0700