Hi there, in this Android SpeedoMeter Tutorial you will learn how to fetch your live speed, distance and duration on your android app as you move around with your phone. This Android SpeedoMeter app makes use of the Google’s Location Service to calculate the speed of your mobile. It’s quite simple the way it functions. You can see the sample output of this Android Speedometer in the below image.
That’s what you are gonna get through in this project. So, Let’s get started with it.
Creating our Android SpeedoMeter
New Android Studio Project
So now lets begin creating our Android Speedometer.
- As usual, begin with creating a new Android Studio Project with empty Activity.
- To access the Google’s Location Service, add google play services dependency to your app level gradle and then sync it.
1 2 3 |
compile 'com.google.android.gms:play-services:8.4.0' |
Creating a Location Service
- Create a class named LocationService.java and add the following code. It consists of a method called updateUI() which updates the live values of the Speed, Distance and Duration.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
package chutka.bitman.com.speedometersimplified; import android.app.Service; import android.content.Intent; import android.location.Location; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; import android.support.annotation.Nullable; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import java.text.DecimalFormat; import java.util.concurrent.TimeUnit; /** * Created by vipul on 12/13/2015. */ public class LocationService extends Service implements LocationListener, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { private static final long INTERVAL = 1000 * 2; private static final long FASTEST_INTERVAL = 1000 * 1; LocationRequest mLocationRequest; GoogleApiClient mGoogleApiClient; Location mCurrentLocation, lStart, lEnd; static double distance = 0; double speed; private final IBinder mBinder = new LocalBinder(); @Nullable @Override public IBinder onBind(Intent intent) { createLocationRequest(); mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); mGoogleApiClient.connect(); return mBinder; } protected void createLocationRequest() { mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(INTERVAL); mLocationRequest.setFastestInterval(FASTEST_INTERVAL); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public void onConnected(Bundle bundle) { try { LocationServices.FusedLocationApi.requestLocationUpdates( mGoogleApiClient, mLocationRequest, this); } catch (SecurityException e) { } } protected void stopLocationUpdates() { LocationServices.FusedLocationApi.removeLocationUpdates( mGoogleApiClient, this); distance = 0; } @Override public void onConnectionSuspended(int i) { } @Override public void onLocationChanged(Location location) { MainActivity.locate.dismiss(); mCurrentLocation = location; if (lStart == null) { lStart = mCurrentLocation; lEnd = mCurrentLocation; } else lEnd = mCurrentLocation; //Calling the method below updates the live values of distance and speed to the TextViews. updateUI(); //calculating the speed with getSpeed method it returns speed in m/s so we are converting it into kmph speed = location.getSpeed() * 18 / 5; } @Override public void onConnectionFailed(ConnectionResult connectionResult) { } public class LocalBinder extends Binder { public LocationService getService() { return LocationService.this; } } //The live feed of Distance and Speed are being set in the method below . private void updateUI() { if (MainActivity.p == 0) { distance = distance + (lStart.distanceTo(lEnd) / 1000.00); MainActivity.endTime = System.currentTimeMillis(); long diff = MainActivity.endTime - MainActivity.startTime; diff = TimeUnit.MILLISECONDS.toMinutes(diff); MainActivity.time.setText("Total Time: " + diff + " minutes"); if (speed > 0.0) MainActivity.speed.setText("Current speed: " + new DecimalFormat("#.##").format(speed) + " km/hr"); else MainActivity.speed.setText("......."); MainActivity.dist.setText(new DecimalFormat("#.###").format(distance) + " Km's."); lStart = lEnd; } } @Override public boolean onUnbind(Intent intent) { stopLocationUpdates(); if (mGoogleApiClient.isConnected()) mGoogleApiClient.disconnect(); lStart = null; lEnd = null; distance = 0; return super.onUnbind(intent); } } |
- Now, when we have created the Location Service, its time to configure the uses of this service and that’ll be done in MainActivity.java. Hence, add the following code to your MainActivity.java. It consists of  methods to bind and unbind  the location service  as per required.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
package chutka.bitman.com.speedometersimplified; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.location.LocationManager; import android.os.Bundle; import android.os.IBinder; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; public class MainActivity extends AppCompatActivity { LocationService myService; static boolean status; LocationManager locationManager; static TextView dist, time, speed; Button start, pause, stop; static long startTime, endTime; ImageView image; static ProgressDialog locate; static int p = 0; private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { LocationService.LocalBinder binder = (LocationService.LocalBinder) service; myService = binder.getService(); status = true; } @Override public void onServiceDisconnected(ComponentName name) { status = false; } }; void bindService() { if (status == true) return; Intent i = new Intent(getApplicationContext(), LocationService.class); bindService(i, sc, BIND_AUTO_CREATE); status = true; startTime = System.currentTimeMillis(); } void unbindService() { if (status == false) return; Intent i = new Intent(getApplicationContext(), LocationService.class); unbindService(sc); status = false; } @Override protected void onResume() { super.onResume(); } @Override protected void onStart() { super.onStart(); } @Override protected void onDestroy() { super.onDestroy(); if (status == true) unbindService(); } @Override public void onBackPressed() { if (status == false) super.onBackPressed(); else moveTaskToBack(true); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dist = (TextView) findViewById(R.id.distancetext); time = (TextView) findViewById(R.id.timetext); speed = (TextView) findViewById(R.id.speedtext); start = (Button) findViewById(R.id.start); pause = (Button) findViewById(R.id.pause); stop = (Button) findViewById(R.id.stop); image = (ImageView) findViewById(R.id.image); start.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //The method below checks if Location is enabled on device or not. If not, then an alert dialog box appears with option //to enable gps. checkGps(); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { return; } if (status == false) //Here, the Location Service gets bound and the GPS Speedometer gets Active. bindService(); locate = new ProgressDialog(MainActivity.this); locate.setIndeterminate(true); locate.setCancelable(false); locate.setMessage("Getting Location..."); locate.show(); start.setVisibility(View.GONE); pause.setVisibility(View.VISIBLE); pause.setText("Pause"); stop.setVisibility(View.VISIBLE); } }); pause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (pause.getText().toString().equalsIgnoreCase("pause")) { pause.setText("Resume"); p = 1; } else if (pause.getText().toString().equalsIgnoreCase("Resume")) { checkGps(); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { //Toast.makeText(this, "GPS is Enabled in your devide", Toast.LENGTH_SHORT).show(); return; } pause.setText("Pause"); p = 0; } } }); stop.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (status == true) unbindService(); start.setVisibility(View.VISIBLE); pause.setText("Pause"); pause.setVisibility(View.GONE); stop.setVisibility(View.GONE); p = 0; } }); } //This method leads you to the alert dialog box. void checkGps() { locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { showGPSDisabledAlertToUser(); } } //This method configures the Alert Dialog box. private void showGPSDisabledAlertToUser() { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this); alertDialogBuilder.setMessage("Enable GPS to use application") .setCancelable(false) .setPositiveButton("Enable GPS", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Intent callGPSSettingIntent = new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivity(callGPSSettingIntent); } }); alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = alertDialogBuilder.create(); alert.show(); } } |
- By now, we are done with the Java part.
Creating User Interface
- Now, we need to configure the activity_main.xml. Add the following code to activity_main.xml which consists of TextViews to display the speed, distance and duration and some buttons.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" android:orientation="vertical"> <ImageView android:id="@+id/image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:src="@mipmap/ic_launcher" android:visibility="gone" /> <TextView android:id="@+id/distancetext" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="100dp" android:gravity="center" android:singleLine="true" android:text="Total Distance:" android:textColor="@android:color/white" android:textSize="25sp" android:textStyle="bold" /> <TextView android:id="@+id/speedtext" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="150dp" android:gravity="center" android:singleLine="true" android:text="Instantaneous Speed:" android:textColor="@android:color/white" android:textSize="18sp" /> <TextView android:id="@+id/timetext" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="190dp" android:gravity="center" android:singleLine="true" android:text="Total Time:" android:textColor="@android:color/white" android:textSize="17sp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" android:orientation="horizontal"> <Button android:id="@+id/start" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="bottom" android:background="@android:color/white" android:paddingBottom="0dp" android:text="Start Calculating" /> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="left" android:layout_weight="1" android:background="@android:color/white" android:text="Pause" android:visibility="gone" /> <Button android:id="@+id/stop" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="right" android:layout_marginLeft="2dp" android:layout_weight="1" android:background="@android:color/white" android:text="Stop" android:visibility="gone" /> </LinearLayout> </FrameLayout> |
- We are almost done. Just update the AndroidManifest.xml and mention the Location Service as it is in the code below.
- Please note that the app will only work below Android Lollipop versions. If you want to make the app work in above Android Lollipop then you need to ask the permissions at run time. To modify this app and ask the permission at run time you can refer to this Android Marshmallow Permission Example Tutorial.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="chutka.bitman.com.speedometersimplified"> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".LocationService"></service> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> </application> </manifest> |
- And you did it. Â Run the app now and hit the start calculating button. It would ask you to enable your gps. As you do, its gonna fetch your location and then show your live speed and all.
- If you are facing some problems then you can download my source code from the link given below.
[sociallocker id=1372] Android SpeedoMeter Example Source Code [/sociallocker]
If you still find some problem, let me know in the comments section I will try my best to sort out your queries. Thank You 🙂