To begin with, the AndroidManifest.xml file contains simply a text area with a canvas beneath it. It also requires location permissions:
<manifest android:versioncode="1" android:versionname="1.0"
package="com.praystation" xmlns:android="http://schemas.android.com/apk/res/android"> <uses-sdk android:minsdkversion="8"> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"> <application android:icon="@drawable/arrow" android:label="@string/app_name"> <activity android:label="@string/app_name" android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"> <category android:name="android.intent.category.LAUNCHER"> & </category> </action> </intent-filter> </activity> </application></uses-permission></uses-permission></uses-sdk></manifest>
I have an 'arrow.png' file in res/drawable for the app logo as well as for the arrow used in this app itself.
The MainActivity finds the best LocationManager and gets your location (assuming you have gps enabled o your device, of course). It also takes in the fixed coordinates of Mecca and calculates the distance and bearing from where you are to where it is:
package com.praystation; import android.app.Activity; import java.util.List; import android.graphics.Canvas; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.location.LocationProvider; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends Activity implements LocationListener { private static final String TAG = "LocationDemo"; private static final String[] S = { "Out of Service", "Temporarily Unavailable", "Available" }; private TextView output; private LocationManager locationManager; private Location mMarkedLocation; private String bestProvider; public static float sBearing = 0.0f; public static float sDistance = 0.0f; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Get the output UI output = (TextView) findViewById(R.id.output); // Get the location manager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); bestProvider = locationManager.getBestProvider(criteria, false); Location location = locationManager.getLastKnownLocation(bestProvider); printLocation(location); mMarkedLocation = new Location("Mecca"); mMarkedLocation.setLatitude(21.4266667); mMarkedLocation.setLongitude(39.8261111); sDistance = location.distanceTo(mMarkedLocation) / 1000; output.append(String.format("\n\ndistance to Mecca: %.2f kilometers\n", sDistance)); sBearing = location.bearingTo(mMarkedLocation); output.append(String.format("\n\nbearing to Mecca: %.2f degrees\n", sBearing)); } /** Register for the updates when Activity is in foreground */ @Override protected void onResume() { super.onResume(); locationManager.requestLocationUpdates(bestProvider, 20000L, 1.0f, this); } /** Stop the updates when Activity is paused */ @Override protected void onPause() { super.onPause(); locationManager.removeUpdates(this); } public void onLocationChanged(Location location) { printLocation(location); } public void onProviderDisabled(String provider) { // let okProvider be bestProvider // re-register for updates // output.append("\n\nProvider Disabled: " + provider); } public void onProviderEnabled(String provider) { // is provider better than bestProvider? // is yes, bestProvider = provider // output.append("\n\nProvider Enabled: " + provider); } public void onStatusChanged(String provider, int status, Bundle extras) { // output.append("\n\nProvider Status Changed: " + provider + ", Status=" // + S[status] + ", Extras=" + extras); } private void printProvider(String provider) { LocationProvider info = locationManager.getProvider(provider); // output.append(info.toString() + "\n\n"); } private void printLocation(Location location) { // if (location == null) // output.append("\nLocation[unknown]\n\n"); // else // output.append("\n\n" + location.toString()); } }
The Panel class does the actual drawing of the canvas, taking the arrow image and rotating it in the direction of the static calculated bearing:
package com.praystation; import java.util.ArrayList; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; public class Panel extends SurfaceView implements SurfaceHolder.Callback{ private CanvasThread canvasthread; public Panel(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub getHolder().addCallback(this); canvasthread = new CanvasThread(getHolder(), this); setFocusable(true); } public Panel(Context context) { super(context); getHolder().addCallback(this); canvasthread = new CanvasThread(getHolder(), this); setFocusable(true); } @Override public void onDraw(Canvas canvas) { Log.d("ondraw", "lefutott"); Paint paint = new Paint(); int isRusty = 1; Bitmap arrow = BitmapFactory.decodeResource(getResources(), R.drawable.arrow2); canvas.drawColor(Color.BLACK); Matrix rotateMatrix = new Matrix(); rotateMatrix.setRotate(MainActivity.sBearing, canvas.getWidth()/2, canvas.getHeight()/2); canvas.drawBitmap(arrow, rotateMatrix, null); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub canvasthread.setRunning(true); canvasthread.start(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub boolean retry = true; canvasthread.setRunning(false); while (retry) { try { canvasthread.join(); retry = false; } catch (InterruptedException e) { // we will try it again and again... } } } }
Finally, we have a CanvasThread class
package com.praystation; import android.graphics.Canvas; import android.view.SurfaceHolder; public class CanvasThread extends Thread { private SurfaceHolder _surfaceHolder; private Panel _panel; private boolean _run = false; public CanvasThread(SurfaceHolder surfaceHolder, Panel panel) { _surfaceHolder = surfaceHolder; _panel = panel; } public void setRunning(boolean run) { _run = run; } @Override public void run() { Canvas c; while (_run) { c = null; try { c = _surfaceHolder.lockCanvas(null); synchronized (_surfaceHolder) { _panel.onDraw(c); } } finally { // do this in a finally so that if an exception is thrown // during the above, we don't leave the Surface in an // inconsistent state if (c != null) { _surfaceHolder.unlockCanvasAndPost(c); } } } } }