C2FGC7K

Implement floating widget like Facebook Chatheads

In 2013 Facebook released a new feature in Facebook Messenger: Chatheads.

Functionality like this was never implemented before and I like it very much. Why?
It’s user-friendly and you can easily dismiss the received messages from your friends with simple drag & drop motion.

Well, I have a good news for you! The implementation of such kind of widget in Android application is not a rocket science. Let’s begin!

Solution:
1) Create a Service (don’t forget to add it in Manifest.xml).

public class ShowHudService extends Service {

    // constants
    public static final String BASIC_TAG = ShowHudService.class.getName();

    // variables
    private WindowManager mWindowManager;
    private Vibrator mVibrator;
    private WindowManager.LayoutParams mPaperParams;
    private WindowManager.LayoutParams mRecycleBinParams;
    private int windowHeight;
    private int windowWidth;

    // UI
    private ImageView ivCrumpledPaper;
    private ImageView ivRecycleBin;

    // get intent methods
    public static Intent getIntent(Context context) {
        Intent intent = new Intent(context, ShowHudService.class);

        return intent;
    }

    // methods
    @Override
    public void onCreate() {
        super.onCreate();

        mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);
        mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        showHud();

        return START_STICKY;
    }

2) Add ImageView (or other View) which you want to be drawn on your window. You must specify exactly the place on the screen via initialization of LayoutParams.

        mPaperParams = new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);

        DisplayMetrics displaymetrics = new DisplayMetrics();
        mWindowManager.getDefaultDisplay().getMetrics(displaymetrics);
        windowHeight = displaymetrics.heightPixels;
        windowWidth = displaymetrics.widthPixels;

        mPaperParams.gravity = Gravity.TOP | Gravity.RIGHT;

        ivCrumpledPaper = new ImageView(this);
        ivCrumpledPaper.setImageResource(R.drawable.ic_crumpled_paper);

        mPaperParams.x = 0;
        mPaperParams.y = 100;

        mWindowManager.addView(ivCrumpledPaper, mPaperParams);
        addCrumpledPaperOnTouchListener();

3) Add OnTouchListener to the View you want to move and then calculate in MotionEvent.ACTION_UP (this is when you drop the View) when you must remove it from the screen (in my case when the crumpled paper is in the recycle bin).

private void addCrumpledPaperOnTouchListener() {
        ivCrumpledPaper.setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        initialX = mPaperParams.x;
                        initialY = mPaperParams.y;
                        initialTouchX = event.getRawX();
                        initialTouchY = event.getRawY();
                        // add recycle bin when moving crumpled paper
                        addRecycleBinView();

                        return true;
                    case MotionEvent.ACTION_UP:

                        int centerOfScreenByX = windowWidth / 2;

                        // remove crumpled paper when the it is in the recycle bin's area
                        if ((mPaperParams.y > windowHeight - ivRecycleBin.getHeight() - ivCrumpledPaper.getHeight()) &&
                                ((mPaperParams.x > centerOfScreenByX - ivRecycleBin.getWidth() - ivCrumpledPaper.getWidth() / 2) && (mPaperParams.x < centerOfScreenByX + ivRecycleBin.getWidth() / 2))) {
                            mVibrator.vibrate(100);
                            stopSelf();
                        }

                        // always remove recycle bin ImageView when paper is dropped
                        mWindowManager.removeView(ivRecycleBin);
                        ivRecycleBin = null;

                        return true;
                    case MotionEvent.ACTION_MOVE:
                        // move paper ImageView
                        mPaperParams.x = initialX + (int) (initialTouchX - event.getRawX());
                        mPaperParams.y = initialY + (int) (event.getRawY() - initialTouchY);
                        mWindowManager.updateViewLayout(ivCrumpledPaper, mPaperParams);
                        return true;
                }
                return false;
            }
        });
    }

4) Don’t forget to add in your Manifest.xml the following code snippet:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

5) Don’t forget to remove all the views you’ve drawn in onDestroy() in your Service:

    @Override
    public void onDestroy() {
        super.onDestroy();

        // remove views on destroy!
        if (ivCrumpledPaper != null) {
            mWindowManager.removeView(ivCrumpledPaper);
            ivCrumpledPaper = null;
        }

        if (ivRecycleBin != null) {
            mWindowManager.removeView(ivRecycleBin);
            ivRecycleBin = null;
        }
    }

6) Here you are the result:
Result

7) You can check out the full code in Github here

Published by

Stefan Tsekov

Android developer @ Dision