Introduction
The very first question you might be thinking is, what are floating widgets in Android. Well, here is the answer - "Floating widgets are simple action buttons that perform some action and always overlay, can float everywhere on the entire screen and are simply draggable -you can leave it anywhere on the screen.
One example of floating widgets is the Facebook chat head bubble button. Uber and Ola driver applications also have these floating widget buttons that can switch from the app to Maps and vice versa.
So here, we are going to create an application in which a floating button widget will be used to switch between Maps and the app itself.
Step 1
First of all, let us create a simple activity and name it MainActivity. You need a service that helps in back navigation from the Maps application to our application. We will see this later in this article.
Step 2
Add the Location Services Gradle in build.gradle.
Step 3
- apply plugin: 'com.android.application'
- android {
- compileSdkVersion 28
- defaultConfig {
- applicationId "yourdomain.floatingwidgetexample"
- minSdkVersion 21
- targetSdkVersion 28
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- }
- dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation 'com.android.support:appcompat-v7:28.0.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.3'
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'com.android.support.test:runner:1.0.2'
- androidTestImplementation 'com.android.support.test.espresso:espresso-
- core:3.0.2'
- implementation 'com.google.android.gms:play-services-location:15.0.1'
- }
Add permissions in the manifest file. Let us see the manifest.xml file.
Step 4
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="yourdomain.floatingwidgetexample">
- <uses-permission android:name="android.permission.INTERNET"/>
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
- <application
- android:allowBackup="false"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/AppTheme"
- tools:ignore="GoogleAppIndexingWarning">
- <activity android:name=".MainActivity">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <service android:name=".FloatWidgetService" />
- </application>
- </manifest>
Create a service named FloatWidgetService.java. See the code below.
Now, we can see that we have inflated a layout in the service whose button will be floating. Let us look into layout_floating_widget.xml
Now, it is time to write some location gathering code to our MainActivity.java since we are gathering the location. We are going to open the Maps app and revert back to our application by simply touching the Service button as we have seen above.
MainActivity.java
activity_main.xml
Now, we can see that the MainActivity consists of a method named getMyLocation(). This method is gathering the location information once we need a location because we want to show the navigation from one place to another place. Destination's latitude, longitude are been taken statically. For the sake of simplicity, you can put your desired latitude, longitude.
Simply, run the application. We get the output screen as below.
Output
The first screen will look like the following.
- public class FloatWidgetService extends Service {
- private WindowManager mWindowManager;
- private View mFloatingWidget;
- public static final String BROADCAST_ACTION = "magicbox";
- private static final int MAX_CLICK_DURATION = 200;
- private long startClickTime;
- public FloatWidgetService() {
- }
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- return START_STICKY;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- mFloatingWidget = LayoutInflater.from(this).inflate(R.layout.layout_floating_widget, null);
- final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
- WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.WRAP_CONTENT,
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
- ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
- : WindowManager.LayoutParams.TYPE_PHONE,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.TRANSLUCENT);
- params.gravity = Gravity.TOP | Gravity.LEFT;
- params.x = 0;
- params.y = 100;
- mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
- mWindowManager.addView(mFloatingWidget, params);
- /* ImageView closeButtonCollapsed = (ImageView) mFloatingWidget.findViewById(R.id.floating_image);
- closeButtonCollapsed.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- *//* Intent intent = new Intent(BROADCAST_ACTION);
- sendBroadcast(intent);
- stopSelf();*//*
- }
- });*/
- mFloatingWidget.findViewById(R.id.root_container).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 = params.x;
- initialY = params.y;
- initialTouchX = event.getRawX();
- initialTouchY = event.getRawY();
- startClickTime = Calendar.getInstance().getTimeInMillis();
- return false;
- case MotionEvent.ACTION_UP:
- int Xdiff = (int) (event.getRawX() - initialTouchX);
- int Ydiff = (int) (event.getRawY() - initialTouchY);
- long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
- if (clickDuration < MAX_CLICK_DURATION) {
- Intent intent = new Intent(BROADCAST_ACTION);
- sendBroadcast(intent);
- stopSelf();
- }
- return false;
- case MotionEvent.ACTION_MOVE:
- params.x = initialX + (int) (event.getRawX() - initialTouchX);
- params.y = initialY + (int) (event.getRawY() - initialTouchY);
- mWindowManager.updateViewLayout(mFloatingWidget, params);
- return false;
- }
- return false;
- }
- });
- }
- @Override
- public void onDestroy() {
- if (mFloatingWidget != null) mWindowManager.removeView(mFloatingWidget);
- super.onDestroy();
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/root_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- tools:ignore="UselessParent">
- <RelativeLayout
- android:id="@+id/collapse_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="visible">
- <ImageView
- android:id="@+id/floating_image"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:adjustViewBounds="true"
- android:scaleType="fitXY"
- android:src="@mipmap/ic_launcher_round" />
- </RelativeLayout>
- </RelativeLayout>
- package yourdomain.floatingwidgetexample;
- import android.Manifest;
- import android.content.ActivityNotFoundException;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.content.IntentSender;
- import android.content.pm.PackageManager;
- import android.location.Location;
- import android.net.Uri;
- import android.os.Build;
- import android.os.Bundle;
- import android.os.Looper;
- import android.provider.Settings;
- import android.support.annotation.NonNull;
- import android.support.v4.app.ActivityCompat;
- import android.support.v7.app.AppCompatActivity;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.TextView;
- import com.google.android.gms.common.api.ResolvableApiException;
- import com.google.android.gms.location.FusedLocationProviderClient;
- import com.google.android.gms.location.LocationCallback;
- import com.google.android.gms.location.LocationRequest;
- import com.google.android.gms.location.LocationResult;
- import com.google.android.gms.location.LocationServices;
- import com.google.android.gms.location.LocationSettingsRequest;
- import com.google.android.gms.location.LocationSettingsResponse;
- import com.google.android.gms.location.SettingsClient;
- import com.google.android.gms.tasks.OnFailureListener;
- import com.google.android.gms.tasks.OnSuccessListener;
- import com.google.android.gms.tasks.Task;
- public class MainActivity extends AppCompatActivity {
- private final static int REQUEST_CODE_LOCATION = 102;
- private static final int REQUEST_CODE_FOR_OVERLAY_SCREEN = 106;
- Button mButton;
- private LocationCallback mLocationCallback;
- Intent startIntent;
- String[] permission = {android.Manifest.permission.ACCESS_FINE_LOCATION};
- private FusedLocationProviderClient mFusedLocationClient;
- private Location mCurrentLocation;
- String destinationLat = "28.6367764";
- String destinationLng = "77.4023482";
- TextView latitudeTextView, longitudeTextView;
- GetFloatingIconClick receiver;
- IntentFilter filter = new IntentFilter();
- private double currentLatitude, currentLongitude;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
- createLocationCallback();
- if (ActivityCompat.checkSelfPermission(MainActivity.this, permission[0]) != PackageManager.PERMISSION_GRANTED) {
- ActivityCompat.requestPermissions(this, new String[]{permission[0]}, REQUEST_CODE_LOCATION);
- } else {
- getMyLocation();
- }
- mButton = (Button) findViewById(R.id.button);
- latitudeTextView = (TextView) findViewById(R.id.latitude_textview);
- longitudeTextView = (TextView) findViewById(R.id.longitude_textview);
- latitudeTextView.setText("Destination latitude = " + destinationLat);
- longitudeTextView.setText("Destination longitude = " + destinationLng);
- mButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(MainActivity.this)) {
- Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:" + getPackageName()));
- startActivityForResult(intent, REQUEST_CODE_FOR_OVERLAY_SCREEN);
- } else {
- initializeView();
- }
- } catch (ActivityNotFoundException e) {
- Uri gmmIntentUri = Uri.parse("google.navigation:q=" + destinationLat + "," + destinationLng + "&mode=d");
- Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
- mapIntent.setPackage("com.google.android.apps.maps");
- startActivity(mapIntent);
- }
- }
- });
- }
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- switch (requestCode) {
- case REQUEST_CODE_LOCATION:
- if (grantResults.length > 0
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- if (ActivityCompat.checkSelfPermission(MainActivity.this,
- Manifest.permission.ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- getMyLocation();
- }
- }
- default:
- break;
- }
- }
- private void createLocationCallback() {
- mLocationCallback = new LocationCallback() {
- @Override
- public void onLocationResult(LocationResult locationResult) {
- super.onLocationResult(locationResult);
- mCurrentLocation = locationResult.getLastLocation();
- mFusedLocationClient.removeLocationUpdates(mLocationCallback);
- updateLocationUI(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
- }
- };
- }
- public void getMyLocation() {
- if (ActivityCompat.checkSelfPermission(MainActivity.this,
- Manifest.permission.ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- final LocationRequest mLocationRequest = new LocationRequest();
- mLocationRequest.setInterval(10000);
- mLocationRequest.setFastestInterval(5000);
- mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
- LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
- .addLocationRequest(mLocationRequest);
- SettingsClient client = LocationServices.getSettingsClient(this);
- Task<LocationSettingsResponse> task = client.checkLocationSettings(builder.build());
- task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
- @Override
- public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
- // All location settings are satisfied. The client can initialize
- // location requests here.
- // ...
- Log.e("location response", locationSettingsResponse.toString());
- if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
- && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
- // TODO: Consider calling
- // ActivityCompat#requestPermissions
- // here to request the missing permissions, and then overriding
- // public void onRequestPermissionsResult(int requestCode, String[] permissions,
- // int[] grantResults)
- // to handle the case where the user grants the permission. See the documentation
- // for ActivityCompat#requestPermissions for more details.
- return;
- }
- mFusedLocationClient.requestLocationUpdates(mLocationRequest,
- mLocationCallback, Looper.myLooper());
- }
- });
- task.addOnFailureListener(this, new OnFailureListener() {
- @Override
- public void onFailure(@NonNull Exception e) {
- if (e instanceof ResolvableApiException) {
- // Location settings are not satisfied, but this can be fixed
- // by showing the user a dialog.
- try {
- // Show the dialog by calling startResolutionForResult(),
- // and check the result in onActivityResult().
- ResolvableApiException resolvable = (ResolvableApiException) e;
- resolvable.startResolutionForResult(MainActivity.this,
- REQUEST_CODE_LOCATION);
- } catch (IntentSender.SendIntentException sendEx) {
- // Ignore the error.
- }
- }
- }
- });
- }
- }
- private void updateLocationUI(Double lat, Double lng) {
- currentLatitude = lat;
- currentLongitude = lng;
- }
- private void initializeView() {
- Uri gmmIntentUri = Uri.parse("google.navigation:q=" + destinationLat + "," + destinationLng + "&mode=d");
- Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
- mapIntent.setPackage("com.google.android.apps.maps");
- startActivity(mapIntent);
- startIntent = new Intent(MainActivity.this, FloatWidgetService.class);
- startService(startIntent);
- }
- @Override
- public void onResume() {
- super.onResume();
- receiver = new GetFloatingIconClick();
- filter.addAction(FloatWidgetService.BROADCAST_ACTION);
- registerReceiver(receiver, filter);
- }
- private class GetFloatingIconClick extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- Intent selfIntent = new Intent(MainActivity.this, MainActivity.class);
- selfIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(selfIntent);
- }
- }
- }
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:context=".MainActivity">
- <TextView
- android:id="@+id/latitude_textview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- tools:text="Latitude : "
- android:layout_alignParentStart="true"
- android:padding="12dp"/>
- <TextView
- android:id="@+id/longitude_textview"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- tools:text="Longitude : "
- android:padding="12dp"
- android:layout_below="@id/latitude_textview"/>
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Go to map"
- android:layout_centerInParent="true" />
- </RelativeLayout>







Join the conversation! Your thoughts help the community grow.