Implementing Speedometer in Android

Posted By : Ravi Sharma | 01-May-2013

Making speedometer work in android can be one tough task since  not much is available on internet.

So in order to create your own app like SpeedTest you can use basic android view ---ImageView.

You will simply need two images one of speedmeter, other of needle. And rotate needle image based on download speed available.

So this blog will be helpful for implementing both speedometer and download manager.

Here is the working code for xml file (speedometer.xml).
 

	<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" >

	

	 <Button

	 android:id="@+id/download"

	        android:layout_width="150dp"

	        android:layout_height="70dp"

	        android:layout_alignParentBottom="true"

	        android:layout_alignParentLeft="true"

	        android:layout_marginBottom="13dp"

	        android:layout_marginLeft="300dp"

	        android:text="Begin Test" />

	

	 <ImageView

	 android:id="@+id/meter"

	 android:layout_width="wrap_content"

	 android:layout_height="wrap_content"

	 android:layout_alignParentLeft="true"

	        android:layout_alignParentTop="true"

	        android:layout_marginLeft="223dp"

	        android:layout_marginTop="26dp"

	        android:src="@drawable/meter" />

	

	    <ImageView

	        android:id="@+id/needle"

	        android:layout_width="130dp"

	        android:layout_height="20dp"

	        android:layout_alignBottom="@+id/meter"

	        android:layout_alignLeft="@+id/download"

	        android:src="@drawable/meter_needle" />

	

	 <TextView

	 android:id="@+id/speed"

	 android:layout_width="wrap_content"

	        android:layout_height="wrap_content"

	        android:layout_alignTop="@+id/meter"

	        android:layout_marginLeft="191dp"

	        android:layout_marginTop="55dp"

	        android:layout_toRightOf="@+id/meter"

	        android:text="TextView"

	        android:textSize="20dp"

	        android:textColor="#000000"/>

	

	</RelativeLayout>

You can adjust UI accordingly I have kept it simple since we are more interested in logic than UI.

You need to paste the images in drawable folder(res->drawable)

Make sure you position the needle correctly so that the rotation is good enough (can take few tests if you work with your own images).

I guess there ain't enough to explain in xml just two ImageViews, one TextView, one button thats it.

Here is the code we are more interested in.

Speedometer.class.

 

 

	package com.example.speedometer;

	

	import java.util.List;

	import android.net.Uri;

	import android.os.AsyncTask;

	import android.os.Build;

	import android.os.Bundle;

	import android.os.Handler;

	import android.annotation.SuppressLint;

	import android.app.Activity;

	import android.app.DownloadManager;

	import android.content.BroadcastReceiver;

	import android.content.Context;

	import android.content.Intent;

	import android.content.IntentFilter;

	import android.content.pm.PackageManager;

	import android.content.pm.ResolveInfo;

	import android.database.Cursor;

	import android.view.View;

	import android.view.animation.RotateAnimation;

	import android.widget.Button;

	import android.widget.ImageView;

	import android.widget.TextView;

	import android.widget.Toast;

	

	public class SpeedometerActivity extends Activity {

	

	ImageView needle;    

	long id;

	Handler h;

	    DownloadManager manager;

	    Button download;

	    Long startTime;

	    int check = 0;

	    String uri;

	    TextView speed;

	

	    @Override

	    protected void onCreate(Bundle savedInstanceState) {

	        super.onCreate(savedInstanceState);

	        setContentView(R.layout.speedometer);

	        download = (Button) findViewById(R.id.download);

	

	    }

	

	    @Override

	    protected void onResume() {

	        super.onResume();

	        h = new Handler();

	        new speedTask().execute();

	        speed = (TextView) findViewById(R.id.speed);

	    }

	

	    public class speedTask extends AsyncTask<Void, String, Void> {

	

	        protected void onPreExecute() {

	

	        }

	

	        protected void onProgressUpdate(final String... message) {

	            try {

	

	                download.setVisibility(View.VISIBLE);

	

	            } catch (Exception e) {

	                e.printStackTrace();

	            }

	

	        }

	

	        @Override

	        protected Void doInBackground(Void... params) {

	            try {

	

	            } catch (Exception e) {

	                e.printStackTrace();

	

	            }

	

	            return null;

	        }

	

	        @Override

	        protected void onPostExecute(Void result) {

	            // TODO Auto-generated method stub

	            super.onPostExecute(result);

	

	            try {

	

	                download.setOnClickListener(new View.OnClickListener() {

	

	                    public void onClick(View arg0) {

	                        downloadFile("testFile",

	                                "https://s3.amazonaws.com/TranscodeAppVideos2/macdonald3.mp4");

	

	                        registerReceiver(broadcast, new IntentFilter(

	                                DownloadManager.ACTION_DOWNLOAD_COMPLETE));

	

	                        startTime = System.currentTimeMillis();

	                        download.setEnabled(false);

	                        check = 1;

	                    }

	

	                });

	

	            } catch (Exception e) {

	                e.printStackTrace();

	

	            }

	

	        }

	

	    }

	

	    @SuppressLint("NewApi")

	    public void downloadFile(String fileName, String downloadUrl) {

	        String DownloadUrl = downloadUrl;

	        DownloadManager.Request request = new DownloadManager.Request(

	                Uri.parse(DownloadUrl.replace("https://", "http://")));

	        request.setDescription("Downloading..."); // appears the same in

	                                                    // Notification bar

	                                                    // while downloading

	        request.setTitle(fileName);

	        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

	            request.allowScanningByMediaScanner();

	            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);

	        }

	        request.setDestinationInExternalPublicDir("/test/", fileName);

	        request.setVisibleInDownloadsUi(true);

	        // get download service and enqueue file

	        manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

	        id = manager.enqueue(request);

	

	        try {

	

	            final Runnable r2 = new Runnable() {

	                public void run() {

	

	                    h.postDelayed(this, 0000);

	

	                    DownloadManager.Query q = new DownloadManager.Query();

	                    q.setFilterById(id);

	                    Cursor cursor = manager.query(q);

	                    cursor.moveToFirst();

	

	                    Integer bytes_downloaded = cursor

	                            .getInt(cursor

	                                    .getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));

	

	                    Long endTime = System.currentTimeMillis();

	                    Long toSeconds = (endTime - startTime);

	

	                    Long downloadedLength = Long.parseLong(bytes_downloaded

	                            .toString());

	                    Long tokB = (downloadedLength) / toSeconds;

	                    Long percentage = (downloadedLength / 11405) / 10;

	                    uri = cursor

	                            .getString(cursor

	                                    .getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));

	                    Integer totalLength = cursor

	                            .getInt(cursor

	                                    .getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

	

	                    if (downloadedLength < totalLength) {

	                        try {

	                            needle = (ImageView) findViewById(R.id.needle);

	                            ((TextView) speed).setText(tokB + " " + "kB/sec");

	                            RotateAnimation rpmNeedleDeflection = new RotateAnimation(

	                                    (float) (((downloadedLength) / toSeconds) / 2.78),

	                                    (float) (((downloadedLength) / toSeconds) / 2.78),

	                                    130, 0);

	                            rpmNeedleDeflection.setDuration(900);

	                            rpmNeedleDeflection.setFillAfter(true);

	                            needle.startAnimation(rpmNeedleDeflection);

	                            needle.refreshDrawableState();

	

	                        } catch (Exception e) {

	

	                        }

	                    }

	                    cursor.close();

	

	                }

	

	            };

	            h.postDelayed(r2, 0000);

	        } catch (Exception e) {

	            Toast.makeText(getBaseContext(), "try again", 3000).show();

	        }

	    }

	

	    public boolean isDownloadManagerAvailable(Context context) {

	        try {

	            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD) {

	                return false;

	            }

	            Intent intent = new Intent(Intent.ACTION_MAIN);

	            intent.addCategory(Intent.CATEGORY_LAUNCHER);

	            intent.setClassName("com.android.providers.downloads.ui",

	                    "com.android.providers.downloads.ui.DownloadList");

	            List<ResolveInfo> list = context.getPackageManager()

	                    .queryIntentActivities(intent,

	                            PackageManager.MATCH_DEFAULT_ONLY);

	            return list.size() > 0;

	        } catch (Exception e) {

	            return false;

	        }

	    }

	

	    BroadcastReceiver broadcast = new BroadcastReceiver() {

	        @Override

	        public void onReceive(Context context, Intent intent) {

	

	            String action = intent.getAction();

	            if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {

	                Long endTime = System.currentTimeMillis();

	

	                Long toSeconds = (endTime - startTime);

	                Long tokB = 11405 / (toSeconds / 1000);

	

	                download.setEnabled(true);

	                RotateAnimation rpmNeedleDeflection = new RotateAnimation(

	                        (float) (tokB / 2.78), (float) (tokB / 2.78), 130, 0);

	                rpmNeedleDeflection.setDuration(1900);

	                rpmNeedleDeflection.setFillAfter(true);

	                needle.startAnimation(rpmNeedleDeflection);

	                needle.refreshDrawableState();

	            }

	

	        }

	

	    };

	

	@Override

	public void onDestroy() {

	if (check == 1)

	            unregisterReceiver(broadcast);

	

	        super.onDestroy();

	    }

	

	}

To start with you got to use background task to download file from internet. In downloadFile() method, You have to query DownloadManager using DownloadManager.Query class so that you can find out the downloaded bytes so far. And based on that you can find the number of seconds and divide to obtain speed. It's android's RotateAnimation that does all the work of rotating image. It requires four parameters, fromDegrees, to Degrees(speed) pivotX , pivotY. I have divided speed furthur by 2.78 so as to accomodate the speed into degrees.This changes based on your image range. And that's all . Just make sure you provide internet, write_external_storage permission as well in your manifest file.

Here is the output

Thanks,

 

Ravi Sharma

[email protected]

 

About Author

Author Image
Ravi Sharma

Ravi Sharma is an Android application developer with experience in Java , Titanium and Phonegap frameworks. Ravi loves drawing and PC games.

Request for Proposal

Name is required

Comment is required

Sending message..