preface

After using MX4Pro for a whole year last year, the biggest impression MEizu left me was the floating ball (I won’t mention the quality issues), swiping left and right to switch apps, pulling up to return to the desktop, pulling down to open the notification bar, tapping back… Everything is silky smooth. However, since the first half of the year changed to S7DEGE, I feel how can not get used to the life without the floating ball.

Samsung has a hoverball feature of its own, but it’s too complicated to use. The hoverball is supposed to be a one-step product, so there’s still a long way to go in software design. So I searched the market for the flyme floating ball, installed all the top floating ball apps, and finally found an app that was almost like Flyme floating ball.

This app has been in my phone for several months and is the only app on my phone besides wechat that allows self-launch. Thanks to the developers of this app, there are no ads and it’s a great port of flyme’s own floating ball.

However, gradually, I felt a little uncomfortable, that is, every time I installed a new APP, when I opened the prompt to grant permissions (storage, photo), the 6.0 system will always warm pop-up box:







Then I had to go to the Settings page, spend half a day finding the hover ball, turn off its “Apps that appear on top” permission, and then go back to the app and grant permission. Finally, I had to go to the Settings page again and spend another half day finding the hover ball and turning on its “Apps that appear at the top” permission. Friend friend, this experience, once is enough, but let me experience N times ah!

But what’s hard to beat about a programmer? I had nothing to do at home this weekend, so I decided to follow my own habits and create the most easy-to-use floating ball in my mind.

design

1.UI

The UI is very simple, three circles are cut directly with Sketch, one is a grey translucent circle for the background, one is a small circle in the center, and another is a large circle hidden by default.

Function of 2.

Because their own operation habits are fixed, so there is no need to add custom operation functions to the suspension ball, directly write the corresponding operation functions.

(1) Click Return

(2) Long press: move the floating ball

(3) Swipe left and swipe right to open the latest application

(4) Pull-up: Returns to the desktop

(5) Pull-down: The first one I defined was the pull-down notification bar, but after a day of use, I added another function to it, which was to keep the pull-down state for 1.5 seconds and remove the floating ball. This way you can easily remove the levitating ball.

implementation

1. How to add floating balls to desktop

First of all, I would like to thank Guo Lin for his “Android desktop Suspension Window Effect implementation, imitating the effect of 360 mobile phone guard suspension window”. This part I refer to this article and successfully added the suspension ball to the desktop.

public static void addBallView(Context context) { if (mBallView == null) { WindowManager windowManager = getWindowManager(context); int screenWidth = windowManager.getDefaultDisplay().getWidth(); int screenHeight = windowManager.getDefaultDisplay().getHeight(); mBallView = new FloatBallView(context); LayoutParams params = new LayoutParams(); params.x = screenWidth; params.y = screenHeight / 2; params.width = WindowManager.LayoutParams.WRAP_CONTENT; params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.gravity = Gravity.LEFT | Gravity.TOP; params.type = LayoutParams.TYPE_PHONE; params.format = PixelFormat.RGBA_8888; params.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; mBallView.setLayoutParams(params); windowManager.addView(mBallView, params); }}Copy the code

2. Gesture judgment

This is the most important part, carrying out the main function of the levitating ball.

(1) when the finger is pressed

When pressed, hide the ball, show the big ball, and record the press position and press time.

case MotionEvent.ACTION_DOWN: mIsTouching = true; mImgBall.setVisibility(INVISIBLE); mImgBigBall.setVisibility(VISIBLE); mLastDownTime = System.currentTimeMillis(); mLastDownX = event.getX(); mLastDownY = event.getY(); postDelayed(new Runnable() { @Override public void run() { if (! mIsLongTouch && mIsTouching && mCurrentMode == MODE_NONE) { mIsLongTouch = isLongClick(event); } } }, LONG_CLICK_LIMIT); break;Copy the code

What does postDealy do at the end of the code? It’s a 300-millisecond delay to determine if it’s a long-press mode. The method code to judge long press is as follows:

private boolean isLongClick(MotionEvent event) { float offsetX = Math.abs(event.getX() - mLastDownX); float offsetY = Math.abs(event.getY() - mLastDownY); long time = System.currentTimeMillis() - mLastDownTime; If (offsetX < mTouchSlop && offsetY < mTouchSlop && time >= LONG_CLICK_LIMIT) {vibrator. Vibrate (mPattern, -1); return true; } else { return false; }}Copy the code

(2) when the finger moves

At this time to determine whether it is in the long press state, if so, then enter the MOVE mode, MOVE the floating ball, if not, then determine the operation gesture, namely pull down or pull up and other gestures.

case MotionEvent.ACTION_MOVE: if (! mIsLongTouch && isTouchSlop(event)) { return true; } if (mIsLongTouch && (mCurrentMode == MODE_NONE || mCurrentMode == MODE_MOVE)) { mLayoutParams.x = (int) (event.getRawX() - mOffsetToParent); mLayoutParams.y = (int) (event.getRawY() - mOffsetToParentY); mWindowManager.updateViewLayout(FloatBallView.this, mLayoutParams); mBigBallX = mImgBigBall.getX(); mBigBallY = mImgBigBall.getY(); mCurrentMode = MODE_MOVE; } else { doGesture(event); } break;Copy the code

The code for gesture operation is as follows, which is mainly calculated according to the current coordinate and the coordinate recorded when pressing, judge gesture, and update the position of the big ball.

private void doGesture(MotionEvent event) { float offsetX = event.getX() - mLastDownX; float offsetY = event.getY() - mLastDownY; if (Math.abs(offsetX) < mTouchSlop && Math.abs(offsetY) < mTouchSlop) { return; } if (Math.abs(offsetX) > Math.abs(offsetY)) { if (offsetX > 0) { if (mCurrentMode == MODE_RIGHT) { return; } mCurrentMode = MODE_RIGHT; mImgBigBall.setX(mBigBallX + OFFSET); mImgBigBall.setY(mBigBallY); } else { if (mCurrentMode == MODE_LEFT) { return; } mCurrentMode = MODE_LEFT; mImgBigBall.setX(mBigBallX - OFFSET); mImgBigBall.setY(mBigBallY); } } else { if (offsetY > 0) { if (mCurrentMode == MODE_DOWN || mCurrentMode == MODE_GONE) { return; } mCurrentMode = MODE_DOWN; mImgBigBall.setX(mBigBallX); mImgBigBall.setY(mBigBallY + OFFSET); // If you hold the drop state for a long time, PostDelayed (new Runnable() {@override public void run() {if (mCurrentMode == MODE_DOWN && mIsTouching) { toRemove(); mCurrentMode = MODE_GONE; } } }, TO_APP_INDEX_LIMIT); } else { if (mCurrentMode == MODE_UP) { return; } mCurrentMode = MODE_UP; mImgBigBall.setX(mBigBallX); mImgBigBall.setY(mBigBallY - OFFSET); }}}Copy the code

(3) when the fingers are lifted

After the finger is lifted, check whether it is in long press mode first. If it is not in long press mode, check whether it is in click mode. If it is not in long press mode, then check whether it is in click mode.

case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
       mIsTouching = false;
       if (mIsLongTouch) {
           mIsLongTouch = false;
       } else if (isClick(event)) {
           AccessibilityUtil.doBack(mService);
       } else {
           doUp();
       }
       mImgBall.setVisibility(VISIBLE);
       mImgBigBall.setVisibility(INVISIBLE);
       mCurrentMode = MODE_NONE;
       break;
Copy the code

The effect

So far, the function of the floating ball has been implemented, let’s see how it works.

One last word

Spent a while after accomplished finally, programmer, the biggest advantage is that they can custom application 😂, apk download address in this: od35ecbnl.bkt.clouddn.com/app-debug.a… , welcome to experience. I also submitted the project to Github: github.com/HalfStackDe… , interested can have a look, if you can incidentally give a star is the best but 😬.

(Please mark ID for reprint: Semi stack engineer, personal blog:halfstackdeveloper.github.io)