commit 202fa24d8dd1e059b8a2fd9dabf60aac947f7e7e Author: Ricky Barrette Date: Fri Feb 3 10:51:05 2012 -0500 init commit Change-Id: Ib96d43f22db2bdf360d1d1e0693f6aa5e3aa50ab diff --git a/DroidFinder/.classpath b/DroidFinder/.classpath new file mode 100644 index 0000000..a7552ca --- /dev/null +++ b/DroidFinder/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/DroidFinder/.project b/DroidFinder/.project new file mode 100644 index 0000000..8a4d233 --- /dev/null +++ b/DroidFinder/.project @@ -0,0 +1,33 @@ + + + DroidFinder + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/DroidFinder/AndroidManifest.xml b/DroidFinder/AndroidManifest.xml new file mode 100644 index 0000000..ed674fe --- /dev/null +++ b/DroidFinder/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DroidFinder/default.properties b/DroidFinder/default.properties new file mode 100644 index 0000000..cd119de --- /dev/null +++ b/DroidFinder/default.properties @@ -0,0 +1,2 @@ +# Project target. +target=android-15 diff --git a/DroidFinder/lint.xml b/DroidFinder/lint.xml new file mode 100644 index 0000000..ee0eead --- /dev/null +++ b/DroidFinder/lint.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/DroidFinder/project.properties b/DroidFinder/project.properties new file mode 100644 index 0000000..9aa0dfa --- /dev/null +++ b/DroidFinder/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=Google Inc.:Google APIs:15 diff --git a/DroidFinder/res/drawable/icon.png b/DroidFinder/res/drawable/icon.png new file mode 100644 index 0000000..a07c69f Binary files /dev/null and b/DroidFinder/res/drawable/icon.png differ diff --git a/DroidFinder/res/drawable/my_location.png b/DroidFinder/res/drawable/my_location.png new file mode 100755 index 0000000..64cff14 Binary files /dev/null and b/DroidFinder/res/drawable/my_location.png differ diff --git a/DroidFinder/res/drawable/quit_menu.png b/DroidFinder/res/drawable/quit_menu.png new file mode 100755 index 0000000..bcd9ec8 Binary files /dev/null and b/DroidFinder/res/drawable/quit_menu.png differ diff --git a/DroidFinder/res/drawable/show_both.png b/DroidFinder/res/drawable/show_both.png new file mode 100755 index 0000000..0ca814d Binary files /dev/null and b/DroidFinder/res/drawable/show_both.png differ diff --git a/DroidFinder/res/drawable/user.png b/DroidFinder/res/drawable/user.png new file mode 100755 index 0000000..ac5c594 Binary files /dev/null and b/DroidFinder/res/drawable/user.png differ diff --git a/DroidFinder/res/layout/map.xml b/DroidFinder/res/layout/map.xml new file mode 100644 index 0000000..44879a7 --- /dev/null +++ b/DroidFinder/res/layout/map.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DroidFinder/res/layout/tabs.xml b/DroidFinder/res/layout/tabs.xml new file mode 100644 index 0000000..1d0e21b --- /dev/null +++ b/DroidFinder/res/layout/tabs.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/DroidFinder/res/values/layer.xml b/DroidFinder/res/values/layer.xml new file mode 100755 index 0000000..e988dd0 --- /dev/null +++ b/DroidFinder/res/values/layer.xml @@ -0,0 +1,7 @@ + + + +Satellite View +Map View + + diff --git a/DroidFinder/res/values/measurement_unit.xml b/DroidFinder/res/values/measurement_unit.xml new file mode 100755 index 0000000..89ebbcf --- /dev/null +++ b/DroidFinder/res/values/measurement_unit.xml @@ -0,0 +1,10 @@ + + + + + +Standard +Metric + + + diff --git a/DroidFinder/res/values/strings.xml b/DroidFinder/res/values/strings.xml new file mode 100644 index 0000000..76fb8d0 --- /dev/null +++ b/DroidFinder/res/values/strings.xml @@ -0,0 +1,5 @@ + + + Hello World, DroidFinder! + Droid Finder + diff --git a/DroidFinder/res/xml/settings.xml b/DroidFinder/res/xml/settings.xml new file mode 100755 index 0000000..adfccdd --- /dev/null +++ b/DroidFinder/res/xml/settings.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java new file mode 100644 index 0000000..8c4c1de --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java @@ -0,0 +1,53 @@ +package com.TwentyCodes.android.DroidFinderFull; + +import android.app.TabActivity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.widget.TabHost; + +/** + * this is the main class for the application, it is responsible for displaying the main tab layout that will display the map and the settings + * pages. + * + * this application will lock the phone, transmit the phones location though SMS messages, set the ringtone stream to max, + * dim the phones display to minimum, and/or ring for a preset period of time based on a designated string received by SMS + * @author ricky barrette + */ +public class DroidFinder extends TabActivity{ + + private PostMortemReportExceptionHandler mExceptionReport = new PostMortemReportExceptionHandler(this);; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + mExceptionReport.run(); + Thread.setDefaultUncaughtExceptionHandler(mExceptionReport); + + super.onCreate(savedInstanceState); + setContentView(R.layout.tabs); + + TabHost tabHost = getTabHost(); // The activity TabHost + TabHost.TabSpec spec; // Resusable TabSpec for each tab + Intent intent; // Reusable Intent for each tab + + // Create an Intent to launch an Activity for the tab (to be reused) + intent = new Intent().setClass(this, MyMapActivity.class); + +// Initialize a TabSpec for each tab and add it to the TabHost + spec = tabHost.newTabSpec("map").setIndicator("Map").setContent(intent); +// res.getDrawable(R.drawable.ic_tab_artists)) + tabHost.addTab(spec); + + intent = new Intent().setClass(this, SettingsActivity.class); + spec = tabHost.newTabSpec("settings").setIndicator("Settings").setContent(intent); + tabHost.addTab(spec); + } + + @Override + public void onPause(){ + MyMapActivity.mMyLocationOverlay.disableCompass(); + MyMapActivity.mMyLocationOverlay.disableMyLocation(); + super.onPause(); + } +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/GeoUtils.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/GeoUtils.java new file mode 100644 index 0000000..9bd18f2 --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/GeoUtils.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2008 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Twenty Codes + * @author ricky barrette + * @author Google Inc. + */ +package com.TwentyCodes.android.DroidFinderFull; + +import java.util.ArrayList; +import java.util.List; + +import android.graphics.Point; + +import com.google.android.maps.GeoPoint; + +/** + * This class contains common tools for computing common geological problems + * @author ricky barrette + * @author Google Inc. + */ +public class GeoUtils { + + private static int EARTH_RADIUS_KM = 6371; + public static double MILLION = 1000000; + + public static int minLatitude; + public static int maxLatitude; + public static int minLongitude; + public static int maxLongitude; + + /** + * computes the bearing of lat2/lon2 in relationship from lat1/lon1 in degrees East + * @param lat1 source lat + * @param lon1 source lon + * @param lat2 destination lat + * @param lon2 destination lon + * @return the bearing of lat2/lon2 in relationship from lat1/lon1 in degrees East + * @author Google Inc. + */ + public static double bearing(double lat1, double lon1, double lat2, double lon2) { + double lat1Rad = Math.toRadians(lat1); + double lat2Rad = Math.toRadians(lat2); + double deltaLonRad = Math.toRadians(lon2 - lon1); + + double y = Math.sin(deltaLonRad) * Math.cos(lat2Rad); + double x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) + * Math.cos(deltaLonRad); + return radToBearing(Math.atan2(y, x)); + } + + /** + * computes the bearing of lat2/lon2 in relationship from lat1/lon1 in degrees East + * @param p1 source geopoint + * @param p2 destination geopoint + * @return the bearing of p2 in relationship from p1 in degrees East + * @author Google Inc. + */ + public static double bearing(GeoPoint p1, GeoPoint p2) { + double lat1 = p1.getLatitudeE6() / MILLION; + double lon1 = p1.getLongitudeE6() / MILLION; + double lat2 = p2.getLatitudeE6() / MILLION; + double lon2 = p2.getLongitudeE6() / MILLION; + + return bearing(lat1, lon1, lat2, lon2); + } + + /** + * Calculates a geopoint x meters away of the geopoint supplied. The new geopoint + * shares the same latitude as geopoint point, this way they are on the same latitude arc. + * + * @param point central geopoint + * @param distance in meters from the geopoint + * @return geopoint that is x meters away from the geopoint supplied + * @author ricky barrette + */ + public static GeoPoint distanceFrom(GeoPoint point, double distance){ + //convert meters into kilometers + distance = distance / 1000; + + // convert lat and lon of geopoint to radians + double lat1Rad = Math.toRadians((point.getLatitudeE6() / 1e6)); + double lon1Rad = Math.toRadians((point.getLongitudeE6() / 1e6)); +// double lat2Rad = lat1Rad; + + /* + * kilometers = acos(sin(lat1Rad)sin(lat2Rad)+cos(lat1Rad)cos(lat2Rad)cos(lon2Rad-lon1Rad)6371 + * + * we are solving this equation for lon2Rad + * + * lon2Rad = lon1Rad+acos(cos(meters/6371)sec(lat1Rad)sec(lat2Rad)-tan(lat1Rad)tan(lat2Rad)) + * + * NOTE: sec(x) = 1/cos(x) + * + * NOTE: that lat2Rad is = lat1Rad because we want to keep the new geopoint on the same lat arc + * therefore i saw no need to create a new variable for lat2Rad, + * and simply inputed lat1Rad in place of lat2Rad in the equation + * + * NOTE: this equation has be tested in the field against another gps device, and the distanceKm() from google + * and has been proven to be damn close + */ + double lon2Rad = lon1Rad + Math.acos( Math.cos((distance/6371)) * (1 / Math.cos(lat1Rad)) + * (1 / Math.cos(lat1Rad)) - Math.tan(lat1Rad) * Math.tan(lat1Rad)); + + /* + * test... this equation is curtisy of Raytheon + * + * KM / 6371*cos(lat1Rad - lat2Rad) + lon1Rad = lon2Rad + * + * NOTE: i ricky, don't think that is is very accurate at all + */ +// double lon2Rad = distance / ( 6371 * Math.cos( (lat1Rad - lat1Rad) ) + lon1Rad); + +// Log.d(tag,"lon2Rad = "+ lon2Rad); +// Log.d(tag,"lon2Deg = "+ Math.toDegrees(lon2Rad)); +// +// Log.d(tag,"distance between the 2 = "+ +// distanceKm(point.getLatitudeE6() / 1e6, point.getLongitudeE6() / 1e6, +// point.getLatitudeE6() / 1e6, Math.toDegrees(lon2Rad))); + + //return a geopoint that is x meters away from the geopoint supplied + return new GeoPoint(point.getLatitudeE6(), (int) (Math.toDegrees(lon2Rad) * 1e6)); + } + + /** + * computes the distance between to lat1/lon1 and lat2/lon2 based on the curve of the earth + * @param lat1 source lat + * @param lon1 source lon + * @param lat2 destination lat + * @param lon2 destination lon + * @return the distance between to lat1/lon1 and lat2/lon2 + * @author Google Inc. + */ + public static double distanceKm(double lat1, double lon1, double lat2, double lon2) { + double lat1Rad = Math.toRadians(lat1); + double lat2Rad = Math.toRadians(lat2); + double deltaLonRad = Math.toRadians(lon2 - lon1); + + return Math.acos(Math.sin(lat1Rad) * Math.sin(lat2Rad) + Math.cos(lat1Rad) * Math.cos(lat2Rad) + * Math.cos(deltaLonRad)) + * EARTH_RADIUS_KM; + } + + /** + * computes the distance between to p1 and p2 based on the curve of the earth + * @param p1 + * @param p2 + * @return the distance between to p1 and p2 + * @author Google Inc. + */ + public static double distanceKm(GeoPoint p1, GeoPoint p2) { + double lat1 = p1.getLatitudeE6() / MILLION; + double lon1 = p1.getLongitudeE6() / MILLION; + double lat2 = p2.getLatitudeE6() / MILLION; + double lon2 = p2.getLongitudeE6() / MILLION; + + return distanceKm(lat1, lon1, lat2, lon2); + } + + /** + * computes a geopoint the is the central geopoint between p1 and p1 + * @param p1 first geopoint + * @param p2 second geopoint + * @return the central geopoint + * @author ricky barrette + */ + public static GeoPoint midPoint(GeoPoint p1, GeoPoint p2) { + minLatitude = (int)(+81 * 1E6); + maxLatitude = (int)(-81 * 1E6); + minLongitude = (int)(+181 * 1E6); + maxLongitude = (int)(-181 * 1E6); + List mPoints = new ArrayList(); + int latitude = p1.getLatitudeE6(); + int longitude = p1.getLongitudeE6(); + if (latitude != 0 && longitude !=0) { + minLatitude = (minLatitude > latitude) ? latitude : minLatitude; + maxLatitude = (maxLatitude < latitude) ? latitude : maxLatitude; + minLongitude = (minLongitude > longitude) ? longitude : minLongitude; + maxLongitude = (maxLongitude < longitude) ? longitude : maxLongitude; + mPoints.add(new Point(latitude, longitude)); + } + + latitude = p2.getLatitudeE6(); + longitude = p2.getLongitudeE6(); + if (latitude != 0 && longitude !=0) { + minLatitude = (minLatitude > latitude) ? latitude : minLatitude; + maxLatitude = (maxLatitude < latitude) ? latitude : maxLatitude; + minLongitude = (minLongitude > longitude) ? longitude : minLongitude; + maxLongitude = (maxLongitude < longitude) ? longitude : maxLongitude; + mPoints.add(new Point(latitude, longitude)); + } + System.gc(); + return new GeoPoint((maxLatitude + minLatitude)/2, (maxLongitude + minLongitude)/2 ); + } + + /** + * converts radians to bearing + * @param rad + * @return bearing + * @author Google Inc. + */ + public static double radToBearing(double rad) { + return (Math.toDegrees(rad) + 360) % 360; + } +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyCustomLocationOverlay.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyCustomLocationOverlay.java new file mode 100644 index 0000000..4c42cc8 --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyCustomLocationOverlay.java @@ -0,0 +1,333 @@ +/** +* @author Twenty Codes +* @author ricky barrette +*/ + +/** + * + */ +package com.TwentyCodes.android.DroidFinderFull; + +import java.text.DecimalFormat; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.SharedPreferences; +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.graphics.Point; +import android.graphics.Paint.Style; +import android.location.Location; +import android.location.LocationProvider; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapView; +import com.google.android.maps.Projection; + +/** + * @author ricky barrette + */ +public class MyCustomLocationOverlay extends com.google.android.maps.MyLocationOverlay { + + protected static boolean blUnit; + protected static GeoPoint gpCar, gpUser; + private static Context mContext; + protected static TextView tvDistance; + private MapView mMapView; + private TextView tvAccuracy; + private SharedPreferences settings; + private ProgressDialog mGPSprogress; + protected static final String STAY_AWAKE = "stay_awake"; + protected static final String MEASUREMENT_UNIT = "measurement_unit"; + protected static final String LAYERS = "layers"; + + /** + * an overlay class that displays the compass of what you want really badly + * AKA your car and a user arrow that points north on the map that automatically updates + * also initializes the textviews used to update user about the location status + * @param context - context to work in + * @param mapView - mapView to post overlay to + * @author ricky barrette + */ + protected MyCustomLocationOverlay(Context context, MapView mapView) { + super(context, mapView); + mContext = context; + tvDistance = (TextView) ((Activity) context).findViewById(R.id.tvDistance2); + tvAccuracy = (TextView) ((Activity) context).findViewById(R.id.tvAccuracy2); + settings = context.getSharedPreferences(SettingsActivity.SETTINGS, 0); + blUnit = settings.getBoolean(MEASUREMENT_UNIT, false); + } + + /** + * setter method for car values. sets geopoint, and location of user respective to car values + * @param car - lat and lon of car + * @author ricky + */ + protected static void setCar(GeoPoint car) { + gpCar = car; + } + + /** + * sets the measurement unit of the distance text field at the top of the activity + * @param unit - true is metric, false is standard + */ + protected static void setUnit(boolean unit) { + blUnit = unit; + Log.i(mContext.getClass().getName(),"unit = "+unit); + } + + /** + * returns a string distance that is based on the users measurement unit preference + * @param distance in kilometers + * @return string distance + * @author ricky barrette + */ + private String distance(double distance) { + DecimalFormat threeDForm = new DecimalFormat("#.###"); + DecimalFormat twoDForm = new DecimalFormat("#.##"); + + /* + * if blnUnit is true, the distance computed will be in metric units, + * else, standard units are used + * meters are used until 1 kilometer is reached, then kilometers are used + * feet are used until 1 mile is reached, then miles are used + */ + if(blUnit){ + if (distance < 1){ + distance = distance * 1000; + return twoDForm.format(distance) +" m"; + } + return threeDForm.format(distance) +" Km"; + } + distance = distance / 1.609344; + if (distance < 1){ + distance = distance * 5280; + return twoDForm.format(distance) +" ft"; + } + return twoDForm.format(distance) +" mi"; + } + + /** + * computes bearing to geopoint based on device oriantaion and draws the compass of what you want really really badly on screen + * @param - canvas - the canvas to draw on + * @param - bearing - bearing of user based on magnetic compass + * @author ricky barrette + */ + @Override + protected void drawCompass(Canvas canvas, float bearing){ + /* + * we moved drawUser() call from drawMyLocation() to here to so draw user + * is updated more often to smooth out the rotation of the arrow in relationship with compass + * @author ricky barrette + * + * we found that the user arrow would only be re-drawn if the map was animating, or if the user was scrolling it, + * so we call mMapView.invalidate() to force the map to be re-dawn, including it's overlays + * @author ricky barrette + * + * if the MapView is not null, then draw the user arrow + */ + if (mMapView != null) { + drawUser(canvas, mMapView, bearing); + mMapView.invalidate(); + } + + /* + * if the car and user geopoint are not null, then draw the compass point to the car geopoint + * + * else draw the compass to point north + */ + if (gpCar != null && gpUser != null){ + Double d = GeoUtils.bearing(gpUser, gpCar); + bearing = bearing - d.floatValue(); + } else if (bearing != 0){ + bearing = 360 - bearing; + } + super.drawCompass(canvas, bearing); + } + + /** + * updates location stats, and the accuracy circle. also saves data needed to draw the user arrow + * @author ricky barrette + * @param - canvas - the canvas to draw on + * @param - mapView - the map which to draw the layout + * @param - lastFix - location object of last fix of gps location + * @param - myLocation - current location of user + * @param - when - Ricky is unsure of this value. please kick him in the balls + */ + @Override + protected void drawMyLocation(Canvas canvas, MapView mapView, Location lastFix, GeoPoint myLocation, long when) { + gpUser = myLocation; + mMapView = mapView; + + tvAccuracy.setText( distance( (lastFix.getAccuracy() / 1000) ) ); + drawAccuracyCircle(myLocation, lastFix, canvas, mapView); + + if (gpCar != null && gpUser != null){ + double distance = GeoUtils.distanceKm(gpUser, gpCar); + //value is set in KM. if user has gone 30 feet from car app is set to check for arrival + if (distance > 0.009144){ +// blnHasLeftCar = true; + } +// //if user has gone back into 30 foot radius and has not found the car and has left the car then notify user of finding of car +// if (distance <= 0.009144 && blnIsCarFound == false && blnHasLeftCar == true){ +// blnIsCarFound = true; +// Vibrator vib = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE); +// AlertDialog.Builder builder = new AlertDialog.Builder(mContext); +// builder.setTitle(mContext.getText(R.string.yay)); +// builder.setMessage(mContext.getText(R.string.found_car)).setCancelable(false) +// .setPositiveButton(mContext.getText(R.string.ok), new DialogInterface.OnClickListener() { +// public void onClick(DialogInterface dialog, int id) { +// +// } +// }); +// vib.vibrate(100); +// builder.show(); +// } + + tvDistance.setText(distance(distance)); + } else { + tvDistance.setText("0"); + } + + } + + /** + * draws an accuracy circle based on current location and lastFix onto the canvas supplied + * @param myLocation - current location of user + * @param lastFix - last gps fix of user + * @param canvas - canvas to draw on + * @param mapView the mapview to draw an overlay for + * @author ricky barrette + */ + private void drawAccuracyCircle(GeoPoint myLocation, Location lastFix, Canvas canvas, MapView mapView) { + Paint paint = new Paint(); + Point center = new Point(); + Point left = new Point(); + Projection projection = mapView.getProjection(); + + /* + * Calculate a geopoint that is "radius" meters away from geopoint point + */ + GeoPoint leftGeo = GeoUtils.distanceFrom(myLocation, lastFix.getAccuracy()); + + /* + * Original method + * + double latitude = lastFix.getLatitude(); + double longitude = lastFix.getLongitude(); + float accuracy = lastFix.getAccuracy(); + + float[] result = new float[1]; + + Location.distanceBetween(latitude, longitude, latitude, longitude + 1, result); + float longitudeLineDistance = result[0]; + + GeoPoint leftGeo = new GeoPoint((int)(latitude * 1e6), (int)((longitude - accuracy / longitudeLineDistance) * 1e6)); + */ + + /* + * Convert the given GeoPoint and leftGeo to onscreen pixel coordinates, + * relative to the top-left of the MapView that provided this Projection. + */ + projection.toPixels(leftGeo, left); + projection.toPixels(myLocation, center); + + /* + * get radius of the circle being drawn by + */ + int circleRadius = center.x - left.x; + if(circleRadius <= 0){ + circleRadius = left.x - center.x; + } + + /* + * paint a blue circle on the map + */ + paint.setAntiAlias(true); + paint.setStrokeWidth(2.0f); + paint.setColor(Color.BLUE); + paint.setStyle(Style.STROKE); + canvas.drawCircle(center.x, center.y, circleRadius, paint); + + /* + * draw a dot over the geopoint + * not really need with this as the user arrow will be the center of the circle + */ +// RectF oval = new RectF(center.x - 1, center.y - 1, center.x + 1, center.y + 1); +// canvas.drawOval(oval, paint); + + /* + * fill the radius with a alpha blue + */ + paint.setAlpha(30); + paint.setStyle(Style.FILL); + canvas.drawCircle(center.x, center.y, circleRadius, paint); + } + + /** + * draws user arrow that points north based on device oriantataion onto the supplied canvas + * @param Canvas - canvas to draw on + * @param mapView the mapview to draw an overlay for + * @param bearing of then compass in degrees East + * @author ricky barrette + */ + private void drawUser(Canvas canvas, MapView mapView, float bearing){ + Point screenPts = mapView.getProjection().toPixels(gpUser, null); + + Bitmap arrowBitmap = BitmapFactory.decodeResource( mContext.getResources(), R.drawable.user); + Matrix matrix = new Matrix(); + matrix.postRotate(bearing); + Bitmap rotatedBmp = Bitmap.createBitmap( + arrowBitmap, + 0, 0, + arrowBitmap.getWidth(), + arrowBitmap.getHeight(), + matrix, + true + ); + + canvas.drawBitmap( + rotatedBmp, + screenPts.x - (rotatedBmp.getWidth() / 2), + screenPts.y - (rotatedBmp.getHeight() / 2), + null + ); + } + + /** + * Called when location provider status is changed. + * this method will be used to display a GPS progress dialog + * when the provider is unavailable, and dismisses the progress dialog is available + * @param provider name of the provider who's status has changed + * @param status of the provider + * @param extras + * @author ricky barrette + */ + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + switch(status){ + case LocationProvider.AVAILABLE: + mGPSprogress.dismiss(); + break; + + case LocationProvider.OUT_OF_SERVICE: + mGPSprogress = ProgressDialog.show(mContext, "", "Acquiring GPS Fix", true); + mGPSprogress.setCancelable(true); + break; + + case LocationProvider.TEMPORARILY_UNAVAILABLE: + mGPSprogress = ProgressDialog.show(mContext, "", "Acquiring GPS Fix", true); + mGPSprogress.setCancelable(true); + break; + } + } + +} diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyMapActivity.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyMapActivity.java new file mode 100644 index 0000000..400dd5a --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyMapActivity.java @@ -0,0 +1,240 @@ +/** +* @author Twenty Codes +* @author ricky barrette +*/ + +package com.TwentyCodes.android.DroidFinderFull; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.location.LocationManager; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; + +import com.google.android.maps.GeoPoint; +import com.google.android.maps.MapActivity; +import com.google.android.maps.MapController; +import com.google.android.maps.MapView; + +/** + * this class handles the map and map functions + * @author ricky barrette + */ +public class MyMapActivity extends MapActivity { + + private MapController mMapController; + public static MyCustomLocationOverlay mMyLocationOverlay; + private MapView mMapView; +// protected PowerManager.WakeLock mWakeLock; +// private List mMapOverlays; + private final String tag = "DroidFinder - MyMapActivity"; + + /** + * displays a dialog to inform that the gps is disabled and provides them with a shortcut to the settings page + * if they select no, then finish() is called. + * @author ricky barrette + */ + private void enableGPSdialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("GPS is disbaled, Do You Want To Enable it?").setCancelable(false) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + Intent callGPSSettingIntent = new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); + startActivity(callGPSSettingIntent); + dialog.cancel(); + } + }) + .setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + finish(); + dialog.cancel(); + } + }); + builder.show(); + } + + /** + * initializes the mapview, it's controller, zoom controls + * @author ricky barrette 3-31-2010 + */ + private void initMap() { + mMapView = (MapView) findViewById(R.id.mapview); + mMapView.setBuiltInZoomControls(true); + mMapController = mMapView.getController(); +// mMapOverlays = mMapView.getOverlays(); + + /* + * add MyCustomLocationOverlay to the map and enable the user icon and the compass + */ + mMyLocationOverlay = new MyCustomLocationOverlay(this, mMapView); + mMapView.getOverlays().add(mMyLocationOverlay); + + /* + * on first fix + * remove the GPS progress dialog, animate map the the users location, + * and then zoom in to zoom level 20 + */ +// mMyLocationOverlay.runOnFirstFix(new Runnable() { +// public void run() { +//// mGPSprogress.dismiss(); +// GeoPoint gpUser = null; +// //try to pan to initial user location, if you fail try, try, try again +// do{ +// gpUser = mMyLocationOverlay.getMyLocation(); +// } while(! panToGeoPoint(gpUser, true)); +// } +// }); + + } + + /** + * loads saved settings from files + * @author ricky barrette + */ + private void loadSettings(){ + //TODO load setting from shared_prefs + } + + @Override + protected void onCreate (Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.map); + initMap(); + + // if the location manager services are not available, then ask user if they want to enable it + if ((LocationManager) getSystemService(Context.LOCATION_SERVICE) == null) + enableGPSdialog(); + } + + @Override + protected void onDestroy() { + mMapController = null; + mMyLocationOverlay = null; + mMapView = null; + System.gc(); + super.onDestroy(); + } + + /** + * pans maps to where the a geopoint is, and if zoomIn is true, zooms in to level 20 + * @param GeoPoint point - lat and lon of point to pan to + * @param boolean zoomIn - true if map needs to be zoomed in + * @return boolean true if panning was successful + * @author ricky barrette + */ + private boolean panToGeoPoint(GeoPoint point, boolean zoomIn) { + if (point != null) { + Log.e(tag,"panToGeoPoint() point was null"); + return false; + } + if (mMapController != null){ + Log.e(tag,"panToGeoPoint() mapControler was null"); + return false; + } + + try { + /** + * We have found that if the map is animating and is then told to animate again it will crash. + * the stopAnimation call should prevent this + */ + mMapController.stopAnimation(false); + mMapController.animateTo(point); + + if(zoomIn){ + mMapController.setZoom(20); + } + } catch (NullPointerException e) { + Log.e(tag,"panToGeoPoint() Nullpointer exceptoin"); + e.printStackTrace(); + return false; + } + + return true; + } + + /** + * displays a quit dialog + * @since 0.0.2 + * @author ricky barrette 3-30-2010 + */ + public void quitDialog(){ + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage("Are you sure you want to quit").setCancelable(false) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + finish(); + } + }) + .setNegativeButton("No", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + builder.show(); + } + + /** + * computes a geopoint the is the central geopoint between the user and the car. + * also it zooms so both marks are visible on the map + * @author ricky barrette + */ + private void showBoth(){ + panToGeoPoint(GeoUtils.midPoint(MyCustomLocationOverlay.gpCar, MyCustomLocationOverlay.gpUser), false); + mMapController.stopAnimation(true); + mMapController.zoomToSpan((GeoUtils.maxLatitude - GeoUtils.minLatitude), (GeoUtils.maxLongitude - GeoUtils.minLongitude)); + } + + /** + * displays toast message with a long duration + * @param msg + * @author ricky barrette + */ + public void toastLong(CharSequence msg) { + Toast toast = Toast.makeText(this, msg, Toast.LENGTH_LONG); + toast.show(); + } + + /** + * displays toast message with a short duration + * @since 0.0.3 + * @param msg + * @author ricky barrette 3-31-2010 + */ + public void toastShort(CharSequence msg) { + Toast toast = Toast.makeText(this, msg, Toast.LENGTH_SHORT); + toast.show(); + } + + /** + * when called this will prevent the screen from sleeping (turning off) + * call this.mWakeLock.release(); to allow screen to sleep again + * @author ricky barrette + * @since 0.0.9 + */ +// private void wakeLock(){ +// if(!mWakeLock.isHeld()){ +// mWakeLock.acquire(); +// } + + @Override + protected boolean isRouteDisplayed() { + return false; + } + + @Override + protected void onPause(){ + mMyLocationOverlay.disableCompass(); + mMyLocationOverlay.disableMyLocation(); + super.onPause(); + } + + @Override + protected void onResume(){ + mMyLocationOverlay.enableCompass(); + mMyLocationOverlay.enableMyLocation(); + super.onResume(); + } +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/PostMortemReportExceptionHandler.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/PostMortemReportExceptionHandler.java new file mode 100644 index 0000000..75d19da --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/PostMortemReportExceptionHandler.java @@ -0,0 +1,211 @@ +package com.TwentyCodes.android.DroidFinderFull; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.reflect.Field; +import java.text.SimpleDateFormat; +import java.util.Date; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Build; + +/** + * dont forget the manifest tag + * + * @author ricky + */ +public class PostMortemReportExceptionHandler implements UncaughtExceptionHandler, Runnable { + public static final String ExceptionReportFilename = "postmortem.trace"; + + private static final String MSG_SUBJECT_TAG = "Exception Report"; //"app title + this tag" = email subject + private static final String MSG_SENDTO = "twentycodes@gmail.com"; //email will be sent to this account + //the following may be something you wish to consider localizing + private static final String MSG_BODY = "Just click send to help make this application better. "+ + "No personal information is being sent (you can check by reading the rest of the email)."; + + private Thread.UncaughtExceptionHandler mDefaultUEH; + private Activity mApp = null; + + public PostMortemReportExceptionHandler(Activity aApp) { + mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler(); + mApp = aApp; + } + + public String getDebugReport(Throwable aException) { + +// NumberFormat theFormatter = new DecimalFormat("#0."); + //stack trace + StackTraceElement[] theStackTrace = aException.getStackTrace(); + + StringBuffer report = new StringBuffer(); + + report.append("--------- Application ---------\n\n"); + + report.append(mApp.getPackageName()+" generated the following exception:\n\n"); + + report.append(aException.toString() + "\n\n"); + + report.append("-------------------------------\n\n"); + + report.append("--------- Stack trace ---------\n\n"); + for (int i = 0; i < theStackTrace.length; i++) { + report.append(" " + theStackTrace[i].toString() + "\n"); + } + report.append("-------------------------------\n\n"); + + //app environment + PackageManager pm = mApp.getPackageManager(); + PackageInfo pi; + try { + pi = pm.getPackageInfo(mApp.getPackageName(), 0); + } catch (NameNotFoundException eNnf) { + //doubt this will ever run since we want info about our own package + pi = new PackageInfo(); + pi.versionName = "unknown"; + pi.versionCode = 69; + } + + Date theDate = new Date(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss_zzz"); + report.append("-------- Environment --------\n"); + report.append("Time\t="+sdf.format(theDate)+"\n"); + report.append("Device\t="+Build.FINGERPRINT+"\n"); + try { + Field theMfrField = Build.class.getField("MANUFACTURER"); + report.append("Make\t="+theMfrField.get(null)+"\n"); + } catch (SecurityException e) { + } catch (NoSuchFieldException e) { + } catch (IllegalArgumentException e) { + } catch (IllegalAccessException e) { + } + report.append("Device: " + Build.DEVICE + "\n"); + report.append("Brand: " + Build.BRAND + "\n"); + report.append("Model: "+Build.MODEL+"\n"); + report.append("Product: "+Build.PRODUCT+"\n"); + report.append("App:\t "+mApp.getPackageName()+", version "+pi.versionName+" (build "+pi.versionCode+")\n"); + report.append("Locale: "+mApp.getResources().getConfiguration().locale.getDisplayName()+"\n"); + report.append("-----------------------------\n\n"); + + report.append("--------- Firmware ---------\n\n"); + report.append("SDK: " + Build.VERSION.SDK + "\n"); + report.append("Release: " + Build.VERSION.RELEASE + "\n"); + report.append("Incremental: " + Build.VERSION.INCREMENTAL + "\n"); + report.append("Build Id: " + Build.ID + "\n"); + report.append("-------------------------------\n\n"); + + // If the exception was thrown in a background thread inside + // AsyncTask, then the actual exception can be found with getCause + report.append("--------- Cause ---------\n\n"); + Throwable cause = aException.getCause(); + if (cause != null) { + report.append(cause.toString() + "\n\n"); + theStackTrace = cause.getStackTrace(); + for (int i = 0; i < theStackTrace.length; i++) { + report.append(" " + theStackTrace[i].toString() + "\n"); + } + } + report.append("-------------------------------\n\n"); + + report.append("--------- Complete Logcat ---------\n\n"); + report.append(getLog().toString()); + report.append("-------------------------------\n\n"); + + report.append("END REPORT"); + + return report.toString(); + } + + protected StringBuilder getLog(){ + final StringBuilder log = new StringBuilder(); + try{ + Process process = Runtime.getRuntime().exec("logcat -d"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + while ((line = bufferedReader.readLine()) != null){ + log.append(line); + log.append("\n"); + } + } + catch (IOException e){ + } + return log; + } + + public void run() { + sendDebugReportToAuthor(); + } + + protected void saveDebugReport(String aReport) { + //save report to file + try { + FileOutputStream theFile = mApp.openFileOutput(ExceptionReportFilename, Context.MODE_PRIVATE); + theFile.write(aReport.getBytes()); + theFile.close(); + } catch(IOException ioe) { + //error during error report needs to be ignored, do not wish to start infinite loop + } + } + + public void sendDebugReportToAuthor() { + String theLine = ""; + StringBuffer theTrace = new StringBuffer(); + try { + BufferedReader theReader = new BufferedReader( + new InputStreamReader(mApp.openFileInput(ExceptionReportFilename))); + while ((theLine = theReader.readLine())!=null) { + theTrace.append(theLine+"\n"); + } + if (sendDebugReportToAuthor(theTrace.toString())) { + mApp.deleteFile(ExceptionReportFilename); + } + } catch (FileNotFoundException eFnf) { + // nothing to do + } catch(IOException eIo) { + // not going to report + } + } + + public Boolean sendDebugReportToAuthor(String aReport) { + if (aReport!=null) { + Intent theIntent = new Intent(Intent.ACTION_SEND); + String theSubject = mApp.getTitle()+" "+MSG_SUBJECT_TAG; + String theBody = "\n"+MSG_BODY+"\n\n"+aReport+"\n\n"; + theIntent.putExtra(Intent.EXTRA_EMAIL,new String[] {MSG_SENDTO}); + theIntent.putExtra(Intent.EXTRA_TEXT, theBody); + theIntent.putExtra(Intent.EXTRA_SUBJECT, theSubject); + theIntent.setType("message/rfc822"); + Boolean hasSendRecipients = (mApp.getPackageManager().queryIntentActivities(theIntent,0).size()>0); + if (hasSendRecipients) { + mApp.startActivity(theIntent); + return true; + } else { + return false; + } + } else { + return true; + } + } + + public void submit(Throwable e) { + String theErrReport = getDebugReport(e); + saveDebugReport(theErrReport); + //try to send file contents via email (need to do so via the UI thread) + mApp.runOnUiThread(this); + } + + public void uncaughtException(Thread t, Throwable e) { + submit(e); + //do not forget to pass this exception through up the chain + mDefaultUEH.uncaughtException(t,e); + } +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SMS.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SMS.java new file mode 100644 index 0000000..8dd0054 --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SMS.java @@ -0,0 +1,208 @@ +/** +* @author Twenty Codes +* @author ricky barrette +*/ + +package com.TwentyCodes.android.DroidFinderFull; + +import java.util.InputMismatchException; +import java.util.Scanner; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.location.Location; +import android.location.LocationManager; +import android.os.Bundle; +import android.telephony.gsm.SmsManager; +import android.telephony.gsm.SmsMessage; +import android.util.Log; + +/** + * this class will handle all receiving and sending of SMS messages + * @author ricky barrette + */ +public class SMS extends BroadcastReceiver{ + + protected static final String DELETE_CONFIRMATION = "delete_comfirmation"; + protected static final String RETURN_ADDRESS = "return_address"; + protected static final String NUMBERS = "numbers"; + protected static final String LAT = "lat"; + protected static final String LON = "lon"; + protected static final String ACCURACY = "accuracy"; + + private SharedPreferences numbers; + private SharedPreferences settings; + private String tag = "SmsReceiver"; + + /** + * checks the md5 hash code of a string msg to the saved md5 hash code of a password + * @param msg + * @param key + * @return true if the md5 hash code matches the saved md5 hash code saved in the passwords.xml + * @author ricky barrette + */ +// private boolean checkmd5sum(String msg, String key){ +// String correctMd5 = passwords.getString(key,null); +// if (correctMd5 != null) { +// String md5hash = DroidFinder.getMd5Hash(msg); +// if (md5hash.equals(correctMd5)) { +// return true; +// } +// } +// return false; +// } + + @Override + public void onReceive(Context context, Intent intent) { + Log.i(tag,"onRecive()"); + + settings = context.getSharedPreferences(SettingsActivity.SETTINGS, 0); + numbers = context.getSharedPreferences(NUMBERS, 0); + + //get the SMS message + Bundle bundle = intent.getExtras(); + + if (bundle != null) + { + SmsMessage[] msgs = null; + StringBuilder smsMsg = new StringBuilder(); + StringBuilder phoneNumber = new StringBuilder(); + + //read the SMS message received + Object[] pdus = (Object[]) bundle.get("pdus"); + msgs = new SmsMessage[pdus.length]; + for (int i=0; i 0 && storeNumber(phoneNumber.toString())){ + Log.i(tag, "saved number successfuly!"); + + Log.i(tag,"sending phone location to sender"); + + LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + Location location = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER); + String number = numbers.getString(RETURN_ADDRESS, null); + if (number != null){ + sendSMS(number, + "DroidFinder: " + + location.getLatitude() + ", " + location.getLongitude() + + "\n Accuracy (meters)= " + location.getAccuracy()); +// + "\n Speed (meters/second) = " +// + location.getSpeed()); + } + + } else { + Log.w(tag, "did not save number successfuly"); + } + } + + /* + * if the sms msg contains the char sequence "DroidFinder: " then save the 3 floats + * for lat, lon, and accuracy. + */ + if (msg.startsWith("DroidFinder")){ + Log.i(tag,"recived a droidfinder message"); + Scanner scan = new Scanner(msg); + int index = 0; + Editor editor = settings.edit(); + + /* + * loops though a human readable message than contains a lat, lon, and accuracy in a specific order. + * + * while the is a other token in the string, try to save it as lat, lon, or accuracy (based on what has been already saved) + * if we cant save due to an InputMismatchException, then log it and skip over the token. + */ + do { + try { + switch (index){ + case 0: + Log.i(tag,"tring to save token as lat"); + editor.putInt(LAT, (int) (scan.nextDouble() * 1e6)); + index++; + Log.i(tag,"save success"); + break; + case 1: + Log.i(tag,"tring to save token as lon"); + editor.putInt(LON, (int) (scan.nextDouble() * 1e6)); + index++; + Log.i(tag,"save success"); + break; + case 2: + Log.i(tag,"tring to save token as accuracy"); + editor.putFloat(ACCURACY, scan.nextFloat()); + index++; + Log.i(tag,"save success"); + break; + } + } catch (InputMismatchException e) { + Log.e(tag,"save failed"); + e.printStackTrace(); + scan.next(); + } + } while(scan.hasNext()); + + editor.commit(); + } + + /* + * lockPattern + * if the SMS message matches a specified string (ignoring case) + * then do something + */ + if (msg.equalsIgnoreCase(settings.getString(SettingsActivity.LOCK_PATTERN_PASS, null))&& + settings.getBoolean(SettingsActivity.LOCK_PATTERN_ENABLED, false)){ + Log.i(tag,"locking phone and dimming screen to save battery"); + + SettingsManager sm = new SettingsManager(context); + + //dim the screen to minimum + sm.setScreenBrightness(0); + //set screen timeout to 1 second + sm.setScreenTimeOff(1); + //lock the screen + sm.setLockPatternEnabled(true); + //disable wifi + sm.setWifiEnabled(false); + } + } + } + + /** + * sends a SMS message + * @param phoneNumber + * @param message + * @author ricky barrette + */ + public void sendSMS(String phoneNumber, String message){ + SmsManager sms = SmsManager.getDefault(); + sms.sendTextMessage(phoneNumber, null, message, null, null); + } + + /** + * stores the senders number to shared_prefs numbers.xml + * @param number + * @return true if save was successful + * @author ricky barrette + */ + private boolean storeNumber(String number){ + Editor editor = numbers.edit(); + editor.putString(RETURN_ADDRESS, number); + return editor.commit(); + } +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java new file mode 100644 index 0000000..008d9a2 --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java @@ -0,0 +1,127 @@ +/** +* @author Twenty Codes +* @author ricky barrette +*/ + +package com.TwentyCodes.android.DroidFinderFull; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceActivity; + +public class SettingsActivity extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener{ + /* + * the following strings are for the shared_prefs passwords.xml + */ + public static final String SETTINGS = "settings"; + public static final String LOCATE_PASS = "locate_pass"; + public static final String LOCK_PATTERN_PASS = "lock_pattern_pass"; + public static final String LOCATE_ENABLED = "locate_enabled"; + public static final String LOCK_PATTERN_ENABLED = "lock_pattern_enabled"; + public static final String RING_PASS = "ring_pass"; + public static final String BACKUP_PASS = "backup_pass"; + public static final String DELETE_PASS = "delete_pass"; + public static final String BACKUP = "backup"; + public static final String RESTORE = "restore"; + private Preference locate_pass; + private Preference lock_pattern_pass; + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //set shared_prefs name + getPreferenceManager().setSharedPreferencesName(SETTINGS); + + //load preferences xml + this.addPreferencesFromResource(R.xml.settings); + + //set onclick listeners + findPreference(BACKUP).setOnPreferenceClickListener(this); + findPreference(RESTORE).setOnPreferenceClickListener(this); + //set onchange listeners + locate_pass = findPreference(LOCATE_PASS); + lock_pattern_pass = findPreference(LOCK_PATTERN_PASS); + locate_pass.setOnPreferenceChangeListener(this); + lock_pattern_pass.setOnPreferenceChangeListener(this); + } + + @Override + protected void onResume() { + super.onResume(); + SharedPreferences shared_prefs = getPreferenceManager().getSharedPreferences(); + + // Setup the initial values + locate_pass.setSummary(shared_prefs.getString(LOCATE_PASS, "")); + lock_pattern_pass.setSummary(shared_prefs.getString(LOCK_PATTERN_PASS, "")); + } + +// @Override +// protected void onPause() { +// super.onPause(); +// // Unregister the listener whenever a key changes +// getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener((OnSharedPreferenceChangeListener) this); +// } + + + /** + * handles onclick events of preferences + * @author ricky barrette + */ + @Override + public boolean onPreferenceClick(Preference preference) { + if(preference.getKey().equals(BACKUP)){ + new SettingsManager(this).backupSystemSettings(); + return true; + } + + if(preference.getKey().equals(RESTORE)){ + new SettingsManager(this).restoreSystemSettings(); + return true; + } + return false; + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if(preference.getKey().equals(LOCATE_PASS)){ + preference.setSummary(newValue.toString()); + return true; + } + + if(preference.getKey().equals(LOCK_PATTERN_PASS)){ + preference.setSummary(newValue.toString()); + return true; + } + return false; + } + + /** + * returns the md5 hash code for a String + * @param input + * @return md5 hash code + * @author ricky barrette + */ +// public static String getMd5Hash(String input) { +// try{ +// MessageDigest md = MessageDigest.getInstance("MD5"); +// byte[] messageDigest = md.digest(input.getBytes()); +// BigInteger number = new BigInteger(1,messageDigest); +// String md5 = number.toString(16); +// +// while (md5.length() < 32) +// md5 = "0" + md5; +// +// return md5; +// } catch(NoSuchAlgorithmException e) { +// Log.e("MD5", e.getMessage()); +// return null; +// } +// } + +} \ No newline at end of file diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsManager.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsManager.java new file mode 100644 index 0000000..6a6c7b6 --- /dev/null +++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsManager.java @@ -0,0 +1,227 @@ +/** +* @author Twenty Codes +* @author ricky barrette +*/ + +package com.TwentyCodes.android.DroidFinderFull; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.net.wifi.WifiManager; +import android.provider.Settings.SettingNotFoundException; + +/** + * this class will handle all backups and changes to the system settings of the phone + * these settings include screen brightness, screen time off, lock screen, wifi, bluetooth + * @author ricky barrette + */ +public class SettingsManager { + + private final String SYSTEM_SETTINGS = "system_settings"; + private final String SCREEN_TIME_OFF = "screen_timer_off"; + private final String SCREEN_BRIGHTNESS = "screen_brightness"; + private final String LOCK_SCREEN_ENABLED = "lock_screen_enabled"; + private final String WIFI_ENABLED = "wifi_enabled"; + private final String BLUETOOTH_ON = "bluetooth_on"; + private final String NETWORK_PREFERENCE = "network_prefrence"; + + private Context mContext; + private SharedPreferences system_settings; + private WifiManager mWifiManager; + + /** + * creates a system settings manager that can modify the following settings: + * screen brightness, screen time off, lock screen, wifi, bluetooth + * @param context + * @author ricky barrette + */ + SettingsManager(Context context){ + mContext = context; + system_settings = context.getSharedPreferences(SYSTEM_SETTINGS, 0); + mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + } + + /** + * saves modified system settings so they can be restored once the phone is recovered + * @return true is save is successful + * @author ricky barrette + */ + public boolean backupSystemSettings(){ + + Editor editor = system_settings.edit(); + + //save screen brightness (0 - 225) + try { + editor.putInt(SCREEN_BRIGHTNESS, getScreenBrightness()); + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + + //save screen time off (milliseconds) + try { + editor.putInt(SCREEN_TIME_OFF, getScreenTimeOff()); + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + + //save lock screen preference + try { + editor.putBoolean(LOCK_SCREEN_ENABLED, getLockPatternEnabled()); + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + + //save bluetooth preference + try { + editor.putBoolean(BLUETOOTH_ON, getBluetoothEnabled()); + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + + //save network preference + try { + editor.putInt(NETWORK_PREFERENCE, getNetworkPreference()); + } catch (SettingNotFoundException e) { + e.printStackTrace(); + } + + //save wifi preference + if (mWifiManager != null) { + editor.putBoolean(WIFI_ENABLED, getWifiEnabled()); + } + + return editor.commit(); + } + + /** + * returns the current state of the bluethooth radio + * @return true if enabled + * @throws SettingNotFoundException + * @author ricky barrette + */ + public boolean getBluetoothEnabled() throws SettingNotFoundException{ + if (android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.BLUETOOTH_ON) == 1) + return true; + return false; + } + + /** + * gets current network preference (this method is not tested) + * @return current network preference + * @throws SettingNotFoundException + * @author ricky barrette + */ + protected int getNetworkPreference() throws SettingNotFoundException{ + return android.provider.Settings.Secure.getInt(mContext.getContentResolver(), + android.provider.Settings.Secure.NETWORK_PREFERENCE); + } + + /** + * returns thecurrent screen brightness on a scale of 0-255 + * @return screen brightness value + * @throws SettingNotFoundException + * @author ricky barrette + */ + protected int getScreenBrightness() throws SettingNotFoundException{ + return android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.System.SCREEN_BRIGHTNESS); + } + + /** + * returns the current state of the lock pattern screen + * @return true if enabled + * @throws SettingNotFoundException + * @author ricky barrette + */ + protected boolean getLockPatternEnabled() throws SettingNotFoundException { + if (android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.System.LOCK_PATTERN_ENABLED) == 1) + return true; + return false; + } + + /** + * returns the current screen time out value in milliseconds + * @return the current screen time out in milliseconds + * @throws SettingNotFoundException + * @author ricky barrette + */ + protected int getScreenTimeOff() throws SettingNotFoundException{ + return android.provider.Settings.System.getInt(mContext.getContentResolver(), + android.provider.Settings.System.SCREEN_OFF_TIMEOUT); + } + + /** + * returns the current state of the wifi + * @return true if enabled + * @author ricky barrette + */ + protected boolean getWifiEnabled(){ + return mWifiManager.isWifiEnabled(); + } + + /** + * enables or disabled bluetooth + * @param enabled + * @author ricky barrette + */ + public void setBluetoothEnabled(boolean enabled){ + android.provider.Settings.Secure.putInt(mContext.getContentResolver(),android.provider.Settings.Secure.BLUETOOTH_ON, + enabled ? 1 : 0); + } + + /** + * set the screen brightness + * @param brightness setting 0-225 + * @author ricky barrette + */ + public void setScreenBrightness(int brightness){ + android.provider.Settings.System.putInt(mContext.getContentResolver(), + android.provider.Settings.System.SCREEN_BRIGHTNESS, brightness); + } + + /** + * enables or disables the lock pattern screen + * @param enabled + * @author ricky barrette + */ + public void setLockPatternEnabled(boolean enabled) { + android.provider.Settings.System.putInt(mContext.getContentResolver(),android.provider.Settings.System.LOCK_PATTERN_ENABLED, + enabled ? 1 : 0); + } + + /** + * sets the screen time off value + * @param milliseconds + * @author ricky barrette + */ + public void setScreenTimeOff(int milliseconds){ + android.provider.Settings.System.putInt(mContext.getContentResolver(), + android.provider.Settings.System.SCREEN_OFF_TIMEOUT, milliseconds); + } + + /** + * enables of disabled wifi + * @param enabled + * @author ricky barrette + */ + public void setWifiEnabled(boolean enabled){ + if (mWifiManager != null){ + mWifiManager.setWifiEnabled(enabled); + } + } + + /** + * restores system settings to backed up values + * @author ricky barrette + */ + public void restoreSystemSettings(){ + setScreenBrightness(system_settings.getInt(SCREEN_BRIGHTNESS, 0)); + setScreenTimeOff(system_settings.getInt(SCREEN_TIME_OFF, 0)); + setLockPatternEnabled(system_settings.getBoolean(LOCK_SCREEN_ENABLED, false)); + setWifiEnabled(system_settings.getBoolean(WIFI_ENABLED, false)); + setBluetoothEnabled(system_settings.getBoolean(BLUETOOTH_ON, false)); + } +} \ No newline at end of file