Floating Chat Heads Using Android Application

Introduction

 
This article demonstrates how to add a floating chat head in your android application using the Android studio and XML code.
 
We all love the chat head or chat bubbles from the popular Facebook Messanger. This provides very handy and easy access to the chat conversation screen no matter which screen you are on. Chat heads are very convenient for multitasking as a user can work and chat at the same time. That means if you are using the calculator app on your phone and if any new message arrives, you can open the conversation and replay the message without switching app.
 
Android
 
Understanding the basics
  • Chat heads are nothing but a floating view that is drawn from other application. Android system allows applications to draw over other application if the application has android.permission.SYSTEM_ALERT_WINDOW permission. 
  • We are going to use the background service to add the floating widget into the view hierarchy of the current screen. So, this floating view is always on top of application windows
  • To, drag the chat head across the screen we are going to override OnTouchListener() to listen to drag events and change the position of the chat has to on the screen.
Let's Start
 
Step 1
 
Create a new project in Android Studio from File >> Project and fill all the necessary details.
 
Next, go to Gradle Scripts >> build.gradle (Module: app).Select build.gradle, The app gradle compile the code and build types will appear. Just replace that the following code. To make a Floating Chat header in your layout XML and add the Floating library in your project or you can also Gradle.
 
Compile Code
  1. compile 'com.github.andremion:counterfab:1.0.1'  
Step 2
 
Next, go to app >> res >> layout >> activity_main.xml.

Android
 
Select activity page. The XML code will appear, Just the following code. Create the layout of the TextView and Button.

Android 
 
Step 3
 
In this step, go to app >> res >> layout >> Right-Click to New >> Layout Resource file. Select Layout Resource page the dialog box will appear create new XML page on given name.
 
Create new XML page, Just replace the following code

Android
 
Step 4

In this step, go to app >> Java >> domain name >> MainActivity.java. Select main activity page, the java code will appear. I have given the code you can use this code or you can use own code 
 
Android
 
MainActivity Code 
  1. package com.journaldev.floatingchatheads;  
  2.   
  3. import android.content.Intent;  
  4. import android.net.Uri;  
  5. import android.os.Build;  
  6. import android.provider.Settings;  
  7. import android.support.v7.app.AppCompatActivity;  
  8. import android.os.Bundle;  
  9. import android.view.View;  
  10. import android.widget.Button;  
  11. import android.widget.TextView;  
  12. import android.widget.Toast;  
  13.   
  14. public class MainActivity extends AppCompatActivity {  
  15.   
  16.   
  17.     private static final int DRAW_OVER_OTHER_APP_PERMISSION = 123;  
  18.     private Button button;  
  19.   
  20.     private TextView textView;  
  21.   
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);  
  27.   
  28.         askForSystemOverlayPermission();  
  29.   
  30.         button = (Button) findViewById(R.id.button);  
  31.         textView = (TextView) findViewById(R.id.textView);  
  32.   
  33.   
  34.         int badge_count = getIntent().getIntExtra("badge_count"0);  
  35.   
  36.         textView.setText(badge_count + " messages received previously");  
  37.   
  38.         button.setOnClickListener(new View.OnClickListener() {  
  39.             @Override  
  40.             public void onClick(View v) {  
  41.                 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(MainActivity.this)) {  
  42.                     startService(new Intent(MainActivity.this, FloatingWidgetService.class));  
  43.                 } else {  
  44.                     errorToast();  
  45.                 }  
  46.             }  
  47.         });  
  48.   
  49.     }  
  50.   
  51.     private void askForSystemOverlayPermission() {  
  52.         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {  
  53.   
  54.             //If the draw over permission is not available to open the settings screen  
  55.             //to grant the permission.  
  56.             Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,  
  57.                     Uri.parse("package:" + getPackageName()));  
  58.             startActivityForResult(intent, DRAW_OVER_OTHER_APP_PERMISSION);  
  59.         }  
  60.     }  
  61.   
  62.   
  63.     @Override  
  64.     protected void onPause() {  
  65.         super.onPause();  
  66.   
  67.   
  68.         // To prevent starting the service if the required permission is NOT granted.  
  69.         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(this)) {  
  70.             startService(new Intent(MainActivity.this, FloatingWidgetService.class).putExtra("activity_background"true));  
  71.             finish();  
  72.         } else {  
  73.             errorToast();  
  74.         }  
  75.     }  
  76.   
  77.   
  78.     @Override  
  79.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  80.         if (requestCode == DRAW_OVER_OTHER_APP_PERMISSION) {  
  81.   
  82.             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {  
  83.                 if (!Settings.canDrawOverlays(this)) {  
  84.                     //Permission is not available. Display error text.  
  85.                     errorToast();  
  86.                     finish();  
  87.                 }  
  88.             }  
  89.         } else {  
  90.             super.onActivityResult(requestCode, resultCode, data);  
  91.         }  
  92.     }  
  93.   
  94.     private void errorToast() {  
  95.         Toast.makeText(this"Draw over other app permission not available. Can't start the application without the permission.", Toast.LENGTH_LONG).show();  
  96.     }  
  97.   
  98. }  
Step 5
 
In this step, go to app >> Java >> domain name >> Right-Click to add >> New >> Java Class. Select the Java Class new dialog box will appear. Create new java page, Just replace the following code. 
 
Android

Android
 
FloatingWidgetService Code
 
Now create a service called FloatingViewService.java. Whenever you want to display a floating view, start the service using startService() command. In onCreate() of the service, we will add the layout of the floating view at the top-left corner of the window.
  1. package com.journaldev.floatingchatheads;  
  2.   
  3. import android.app.Service;  
  4. import android.content.Intent;  
  5. import android.graphics.PixelFormat;  
  6. import android.graphics.Point;  
  7. import android.os.IBinder;  
  8. import android.support.annotation.Nullable;  
  9. import android.view.Display;  
  10. import android.view.Gravity;  
  11. import android.view.LayoutInflater;  
  12. import android.view.MotionEvent;  
  13. import android.view.View;  
  14. import android.view.ViewTreeObserver;  
  15. import android.view.WindowManager;  
  16. import android.widget.RelativeLayout;  
  17. import com.andremion.counterfab.CounterFab;  
  18.   
  19. public class FloatingWidgetService extends Service {  
  20.   
  21.   
  22.     private WindowManager mWindowManager;  
  23.     private View mOverlayView;  
  24.     int mWidth;  
  25.     CounterFab counterFab;  
  26.     boolean activity_background;  
  27.   
  28.   
  29.     @Nullable  
  30.     @Override  
  31.     public IBinder onBind(Intent intent) {  
  32.         return null;  
  33.     }  
  34.   
  35.   
  36.     @Override  
  37.     public int onStartCommand(Intent intent, int flags, int startId) {  
  38.   
  39.         if (intent != null) {  
  40.             activity_background = intent.getBooleanExtra("activity_background"false);  
  41.   
  42.         }  
  43.   
  44.         if (mOverlayView == null) {  
  45.   
  46.             mOverlayView = LayoutInflater.from(this).inflate(R.layout.overlay_layout, null);  
  47.   
  48.   
  49.             final WindowManager.LayoutParams params = new WindowManager.LayoutParams(  
  50.                     WindowManager.LayoutParams.WRAP_CONTENT,  
  51.                     WindowManager.LayoutParams.WRAP_CONTENT,  
  52.                     WindowManager.LayoutParams.TYPE_PHONE,  
  53.                     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,  
  54.                     PixelFormat.TRANSLUCENT);  
  55.   
  56.   
  57.             //Specify the view position  
  58.             params.gravity = Gravity.TOP | Gravity.LEFT;        //Initially view will be added to top-left corner  
  59.             params.x = 0;  
  60.             params.y = 100;  
  61.   
  62.   
  63.             mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);  
  64.             mWindowManager.addView(mOverlayView, params);  
  65.   
  66.             Display display = mWindowManager.getDefaultDisplay();  
  67.             final Point size = new Point();  
  68.             display.getSize(size);  
  69.   
  70.             counterFab = (CounterFab) mOverlayView.findViewById(R.id.fabHead);  
  71.             counterFab.setCount(1);  
  72.   
  73.   
  74.             final RelativeLayout layout = (RelativeLayout) mOverlayView.findViewById(R.id.layout);  
  75.             ViewTreeObserver vto = layout.getViewTreeObserver();  
  76.             vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {  
  77.                 @Override  
  78.                 public void onGlobalLayout() {  
  79.                     layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);  
  80.                     int width = layout.getMeasuredWidth();  
  81.   
  82.                     //To get the accurate middle of the screen we subtract the width of the floating widget.  
  83.   
  84.                     mWidth = size.x - width;  
  85.   
  86.                 }  
  87.             });  
  88.   
  89.              
OnTouchListener()
 
To drag the chat head along with the user’s touch, we have to override OnTouchListener(). Whenever the user touches the chat head, we will record the initial x and y coordinates, and when the user moves the finger, the application will calculate the new X and Y coordinate and move the chat head.

Also, implement click listener to close the chat head by stopping the service when the user clicks on the close icon at the top-right of the chat head

Android
 
TouchListener Code
  1. counterFab.setOnTouchListener(new View.OnTouchListener() {  
  2.                 private int initialX;  
  3.                 private int initialY;  
  4.                 private float initialTouchX;  
  5.                 private float initialTouchY;  
  6.   
  7.                 @Override  
  8.                 public boolean onTouch(View v, MotionEvent event) {  
  9.                     switch (event.getAction()) {  
  10.                         case MotionEvent.ACTION_DOWN:  
  11.   
  12.                             //remember the initial position.  
  13.                             initialX = params.x;  
  14.                             initialY = params.y;  
  15.   
  16.   
  17.                             //get the touch location  
  18.                             initialTouchX = event.getRawX();  
  19.                             initialTouchY = event.getRawY();  
  20.   
  21.   
  22.                             return true;  
  23.                         case MotionEvent.ACTION_UP:  
  24.   
  25.                             //Only start the activity if the application is in background. Pass the current badge_count to the activity  
  26.                             if (activity_background) {  
  27.   
  28.                                 float xDiff = event.getRawX() - initialTouchX;  
  29.                                 float yDiff = event.getRawY() - initialTouchY;  
  30.   
  31.                                 if ((Math.abs(xDiff) < 5) && (Math.abs(yDiff) < 5)) {  
  32.                                     Intent intent = new Intent(FloatingWidgetService.this, MainActivity.class);  
  33.                                     intent.putExtra("badge_count", counterFab.getCount());  
  34.                                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  35.                                     startActivity(intent);  
  36.   
  37.                                     //close the service and remove the fab view  
  38.                                     stopSelf();  
  39.                                 }  
  40.   
  41.                             }  
  42.                             //Logic to auto-position the widget based on where it is positioned currently w.r.t middle of the screen.  
  43.                             int middle = mWidth / 2;  
  44.                             float nearestXWall = params.x >= middle ? mWidth : 0;  
  45.                             params.x = (int) nearestXWall;  
  46.   
  47.   
  48.                             mWindowManager.updateViewLayout(mOverlayView, params);  
  49.   
  50.   
  51.                             return true;  
  52.                         case MotionEvent.ACTION_MOVE:  
  53.   
  54.   
  55.                             int xDiff = Math.round(event.getRawX() - initialTouchX);  
  56.                             int yDiff = Math.round(event.getRawY() - initialTouchY);  
  57.   
  58.   
  59.                             //Calculate the X and Y coordinates of the view.  
  60.                             params.x = initialX + xDiff;  
  61.                             params.y = initialY + yDiff;  
  62.   
  63.                             //Update the layout with new X & Y coordinates  
  64.                             mWindowManager.updateViewLayout(mOverlayView, params);  
  65.   
  66.   
  67.                             return true;  
  68.                     }  
  69.                     return false;  
  70.                 }  
  71.             });  
  72.         } else {  
  73.   
  74.             counterFab.increase();  
  75.   
  76.         }  
  77.   
  78.   
  79.         return super.onStartCommand(intent, flags, startId);  
  80.   
  81.   
  82.     }  
  83.   
  84.     @Override  
  85.     public void onCreate() {  
  86.         super.onCreate();  
  87.   
  88.         setTheme(R.style.AppTheme);  
  89.   
  90.   
  91.     }  
  92.   
  93.     @Override  
  94.     public void onDestroy() {  
  95.         super.onDestroy();  
  96.         if (mOverlayView != null)  
  97.             mWindowManager.removeView(mOverlayView);  
  98.     }  
  99.   
  100. }  
Step 6

Next, go to app >> manifests >> AndroidManifest.XML, Add alert window permission to the AndroidManifest.XML file. This permission allows an app to create windows, a show on top of all other apps. We'll also add a service named  FloatingChatHeader that will be created shortly.
 
Android
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.journaldev.floatingchatheads">  
  4.   
  5.     <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />  
  6.     <uses-permission android:name="TASKS"/>  
  7.   
  8.     <application  
  9.         android:allowBackup="true"  
  10.         android:icon="@mipmap/ic_launcher"  
  11.         android:label="@string/app_name"  
  12.         android:roundIcon="@mipmap/ic_launcher_round"  
  13.         android:supportsRtl="true"  
  14.         android:theme="@style/AppTheme">  
  15.         <activity android:name=".MainActivity">  
  16.             <intent-filter>  
  17.                 <action android:name="android.intent.action.MAIN" />  
  18.   
  19.                 <category android:name="android.intent.category.LAUNCHER" />  
  20.             </intent-filter>  
  21.         </activity>  
  22.         <service  
  23.             android:name=".FloatingWidgetService"  
  24.             android:enabled="true"  
  25.             android:exported="false" />  
  26.      </application>  
  27. </manifest>  
Step 7
 
Next, go to Android Studio and Deploy the application, Select Emulator or your Android mobile with USB debugging enabled. Give it a few sec to make installations and set permission 
 
Run the application in your desired emulator (Shift + F10)
 
Android

Finally, we have successfully created Floating Chat head application. Later we will discuss more Android Applications.