|
|||||||||||
|
A Three-Dimensional Compass for the Android Phone
Class Hierarchy*
This is the main Activity class that is run when the application is started. It takes care of setting up and updating the accelerometer and location sensors, as well as handling starting and pausing the application. It also lays out the physical appearance of the screen; this is done in a different manner than the way that is usually done with the XML. Instead, it puts a LinearLayout inside another LinearLayout, which sets up the overall layout look. package com.lichard49.Compass; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.widget.LinearLayout; /** * The main initial Activity that is started when the application begins * Sets up location and acceleration sensors * Sets up layout * * @author Richard * */ public class Compass extends Activity implements SensorEventListener, LocationListener { //Fields dealing with display classes Draw draw; Draw2 draw2; Draw3 draw3; //Fields dealing with location sensor LocationManager locationManager; String bestProvider; //Fields dealing with acceleration sensor Sensor sensor; SensorManager sensorManager; final int rate = SensorManager.SENSOR_DELAY_GAME; /** * Method called at startup * * @param savedInstanceState Satisfies "extends Activity" */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //accel sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); //GPS (location) locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); bestProvider = locationManager.getBestProvider(criteria, false); //drawing and display draw = new Draw(this); draw2 = new Draw2(this); draw3 = new Draw3(this); //layout LinearLayout view = new LinearLayout(this); view.setOrientation(LinearLayout.VERTICAL); LinearLayout topView = new LinearLayout(this); topView.setOrientation(LinearLayout.HORIZONTAL); topView.addView(draw3, 160, 185); topView.addView(draw2, 160, 185); view.addView(topView, 320, 185); view.addView(draw, 320, 245); setContentView(view); } ///////////////// Basics /** * Called when the application is in the background */ public void onPause() { super.onPause(); sensorManager.unregisterListener(this); locationManager.removeUpdates(this); //this is important because Android applications //do not close, they are in the background //so resources would be hogged otherwise } /** * Called when the application is started * or in the foreground again */ public void onResume() { super.onResume(); sensorManager.registerListener(this, sensor, rate); locationManager.requestLocationUpdates( bestProvider, 0, 0, this); } //==================Accel===================== /** * Called when the values of the acceleration sensor changes * * @param e Details about the change */ public void onSensorChanged(SensorEvent e) { //update Base.accelValues[0] = e.values[0]; Base.accelValues[1] = (-1*Math.abs(e.values[2])+90)/90; Base.accelValues[2] = (-1*Math.abs(e.values[1])+90)/90; //refresh displays draw.invalidate(); draw2.invalidate(); draw3.invalidate(); } /** * Called when accuracy of the sensor is changed * * @param sen Which sensor's accuracy changed * @param acc The new accuracy degree */ public void onAccuracyChanged(Sensor sen, int acc) { } //==================GPS===================== /** * Called when the location changes * * @param location The new location */ public void onLocationChanged(Location location) { //update location if(location != null) Base.location = new Coordinate(location.getLongitude(), location.getLatitude()); } /** * Called when the provider of location (satellite) * is disabled * * @param arg0 The provider */ public void onProviderDisabled(String arg0) { } /** * Called when the provider of location is enabled * * @param arg0 The provider */ public void onProviderEnabled(String arg0) { } /** * Called when status of the provider of location changes * * @param arg0 The provider * @param arg1 The status * @param arg2 Additional information */ public void onStatusChanged(String arg0, int arg1, Bundle arg2) { } }
This class takes care of drawing the compass image, and extends ImageView to do this. It orients the image in the correct direction by getting the accelerometer values, and using the rotate() method to rotate the canvas. This class also stores the screen size whenever the screen size is changed, such as at startup. Not only does this make it adaptable to any phone, but it also reduces the number of times the program has to get the dimension.
package com.lichard49.Compass; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.widget.ImageView; /** * Draws the compass image * Uses acceleration information to rotate the image * * @author Richard * */ public class Draw extends ImageView { Paint paint; Bitmap compass; /** * constructor * @param context Activity context for a View */ public Draw(Context context) { super(context); paint = new Paint(); //get image of compass compass = BitmapFactory.decodeResource( context.getResources(), R.drawable.compass); } /** * similar to "paint" method, invalidate is used to "repaint" * * @param g Canvas object */ public void onDraw(Canvas g) { super.onDraw(g); g.drawColor(Color.WHITE); //rotate affects everything afterwards g.rotate(-1*Base.accelValues[0], Base.width/2, Base.height/2); g.drawBitmap(compass, Base.width/2 - compass.getWidth()/2, Base.height/2 - compass.getHeight()/2, paint); } /** * store screen width and height at start up so onDraw method * does not need to repeatedly get dimensions (efficiency) * * @param w New width * @param h New height * @param oldW Old width * @param oldH Old height */ public void onSizeChanged(int w, int h, int oldW, int oldH) { Base.width = w; Base.height = h; } }
These two classes act in pretty much the same way as Draw does. They display the sensor values and rotate them so they are parallel to the ground.
This class just simplifies keeping track of the location sensor values. It just ties two doubles together and makes them easily accesible.
package com.lichard49.Compass; /** * Ties two doubles together * Simplifies location storing * * @author Richard * */ public class Coordinate { //public so they can be directly accessed public double x; public double y; /** * Constructor to pass in values and easily * constrcut a coordinate * * @param xx X coordinate * @param yy Y coordinate */ public Coordinate(double xx, double yy) { x = xx; y = yy; } /** * getString for easy output * * @return String The coordinates */ public String toString() { return x + ", " + y; } }
This class also simplifies information storing and accessing by making the sensor values accessible to all classes, through static.
package com.lichard49.Compass; /** * Static class to make these values accessable * to all classes * Holds values for location, acceleration, * width and height of the screen * * @author Richard * */ public class Base { //[0] = z, [1] = x, [2] = y public static float[] accelValues = new float[3]; public static Coordinate location = new Coordinate(0, 0); public static int width; public static int height; }
Some additional notes about the XML file: it is the one genarated by Eclipse when creating a new application. The only modification made was adding the line <uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION"/> in order to enable the location sensor.
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lichard49.Compass" android:versionCode="1" android:versionName="1.0"> <!-- This allows use of the location sensor --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Compass" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
About the Programmer: Richard Li is a junior at Southside High School in Greenville, SC. He is a student in Mr. Rogers' IB Computer Science class and enjoys programming and music.
Bold denotes a class, italics denote a method, underline denotes an object (home) |