diff --git a/DroidFinder/.classpath b/DroidFinder/.classpath
index a7552ca..b8dae0a 100644
--- a/DroidFinder/.classpath
+++ b/DroidFinder/.classpath
@@ -3,5 +3,7 @@
+
+
diff --git a/DroidFinder/AndroidManifest.xml b/DroidFinder/AndroidManifest.xml
index ed674fe..1f02307 100644
--- a/DroidFinder/AndroidManifest.xml
+++ b/DroidFinder/AndroidManifest.xml
@@ -1,43 +1,50 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/DroidFinder/assets/exceptionhandler.properties b/DroidFinder/assets/exceptionhandler.properties
new file mode 100644
index 0000000..9f1c60a
--- /dev/null
+++ b/DroidFinder/assets/exceptionhandler.properties
@@ -0,0 +1,22 @@
+# exceptionhandler.properties
+# @author ricky barrette
+# @author twenty codes
+
+# This file is used to tell the Exception Handler LIbrary how to file
+# new exception reports
+# HTTP ONLY
+#
+# Place this file in you project's assets folder and edit as needed
+#
+# server is the physical web address for your server
+# file is the path to your filing script
+# get is the path to your json retrieval script
+# app is the redmine project name
+# tracker is the redmine tracker
+server = http://rickbarrette.dyndns.org:8080/redmine/exceptionhandler
+app = test
+tracker = Development Bug
+
+# uncomment the following if you want your application to use email to file reports.
+# if this is uncommented, email will always be used.
+#email = twentycodes@gmail.com
\ No newline at end of file
diff --git a/DroidFinder/project.properties b/DroidFinder/project.properties
index 9aa0dfa..ed7c222 100644
--- a/DroidFinder/project.properties
+++ b/DroidFinder/project.properties
@@ -9,3 +9,5 @@
# Project target.
target=Google Inc.:Google APIs:15
+android.library.reference.1=../../exception_handler_library/ExceptionHandlerLib
+android.library.reference.2=../../location_library/LocationLib
diff --git a/DroidFinder/res/layout/tabs.xml b/DroidFinder/res/layout/tabs.xml
index 1d0e21b..3999bde 100644
--- a/DroidFinder/res/layout/tabs.xml
+++ b/DroidFinder/res/layout/tabs.xml
@@ -2,20 +2,30 @@
+ android:layout_height="fill_parent" >
+
+ android:orientation="vertical"
+ android:padding="5dp" >
+
+
+ android:padding="5dp" >
+
+
+
+
\ No newline at end of file
diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java
index 8c4c1de..6052330 100644
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java
+++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/DroidFinder.java
@@ -1,11 +1,13 @@
package com.TwentyCodes.android.DroidFinderFull;
-import android.app.TabActivity;
-import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.view.ViewPager;
import android.widget.TabHost;
+import com.TwentyCodes.android.exception.ExceptionHandler;
+import com.TwentyCodes.android.location.UserOverlayMapFragment;
+
/**
* 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.
@@ -14,40 +16,38 @@ import android.widget.TabHost;
* 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);;
+public class DroidFinder extends FragmentActivity{
+ private TabHost mTabHost;
+ private ViewPager mViewPager;
+ private TabsAdapter mTabsAdapter;
+
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
- mExceptionReport.run();
- Thread.setDefaultUncaughtExceptionHandler(mExceptionReport);
-
+ Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
+
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
+ mTabHost = (TabHost)findViewById(android.R.id.tabhost);
+ mTabHost.setup();
- // Create an Intent to launch an Activity for the tab (to be reused)
- intent = new Intent().setClass(this, MyMapActivity.class);
+ mViewPager = (ViewPager)findViewById(R.id.pager);
-// 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);
+ mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
+
+ mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), UserOverlayMapFragment.class, null);
+// mTabsAdapter.addTab(mTabHost.newTabSpec("settings").setIndicator("Settings"), SettingsFragment.class, null);
- intent = new Intent().setClass(this, SettingsActivity.class);
- spec = tabHost.newTabSpec("settings").setIndicator("Settings").setContent(intent);
- tabHost.addTab(spec);
+ if (savedInstanceState != null) {
+ mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
+ }
}
-
+
@Override
- public void onPause(){
- MyMapActivity.mMyLocationOverlay.disableCompass();
- MyMapActivity.mMyLocationOverlay.disableMyLocation();
- super.onPause();
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString("tab", mTabHost.getCurrentTabTag());
}
}
\ 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
deleted file mode 100644
index 9bd18f2..0000000
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/GeoUtils.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 4c42cc8..0000000
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyCustomLocationOverlay.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/**
-* @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
deleted file mode 100644
index 400dd5a..0000000
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/MyMapActivity.java
+++ /dev/null
@@ -1,240 +0,0 @@
-/**
-* @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
deleted file mode 100644
index 75d19da..0000000
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/PostMortemReportExceptionHandler.java
+++ /dev/null
@@ -1,211 +0,0 @@
-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
index 8dd0054..334aa3e 100644
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SMS.java
+++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SMS.java
@@ -59,7 +59,7 @@ public class SMS extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
Log.i(tag,"onRecive()");
- settings = context.getSharedPreferences(SettingsActivity.SETTINGS, 0);
+ settings = context.getSharedPreferences(SettingsFragment.SETTINGS, 0);
numbers = context.getSharedPreferences(NUMBERS, 0);
//get the SMS message
@@ -87,8 +87,8 @@ public class SMS extends BroadcastReceiver{
* if the SMS message matches a specified string (ignoring case)
* then do something
*/
- if (msg.equalsIgnoreCase(settings.getString(SettingsActivity.LOCATE_PASS, null)) &&
- settings.getBoolean(SettingsActivity.LOCATE_ENABLED, false)){
+ if (msg.equalsIgnoreCase(settings.getString(SettingsFragment.LOCATE_PASS, null)) &&
+ settings.getBoolean(SettingsFragment.LOCATE_ENABLED, false)){
if (phoneNumber.toString().length() > 0 && storeNumber(phoneNumber.toString())){
Log.i(tag, "saved number successfuly!");
@@ -165,8 +165,8 @@ public class SMS extends BroadcastReceiver{
* 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)){
+ if (msg.equalsIgnoreCase(settings.getString(SettingsFragment.LOCK_PATTERN_PASS, null))&&
+ settings.getBoolean(SettingsFragment.LOCK_PATTERN_ENABLED, false)){
Log.i(tag,"locking phone and dimming screen to save battery");
SettingsManager sm = new SettingsManager(context);
diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsFragment.java
similarity index 92%
rename from DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java
rename to DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsFragment.java
index 008d9a2..9eb6a90 100644
--- a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsActivity.java
+++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/SettingsFragment.java
@@ -6,14 +6,13 @@
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;
+import android.preference.PreferenceFragment;
-public class SettingsActivity extends PreferenceActivity implements OnPreferenceClickListener, OnPreferenceChangeListener{
+public class SettingsFragment extends PreferenceFragment implements OnPreferenceClickListener, OnPreferenceChangeListener{
/*
* the following strings are for the shared_prefs passwords.xml
*/
@@ -52,7 +51,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
}
@Override
- protected void onResume() {
+ public void onResume() {
super.onResume();
SharedPreferences shared_prefs = getPreferenceManager().getSharedPreferences();
@@ -76,12 +75,12 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference
@Override
public boolean onPreferenceClick(Preference preference) {
if(preference.getKey().equals(BACKUP)){
- new SettingsManager(this).backupSystemSettings();
+ new SettingsManager(this.getActivity()).backupSystemSettings();
return true;
}
if(preference.getKey().equals(RESTORE)){
- new SettingsManager(this).restoreSystemSettings();
+ new SettingsManager(this.getActivity()).restoreSystemSettings();
return true;
}
return false;
diff --git a/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/TabsAdapter.java b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/TabsAdapter.java
new file mode 100644
index 0000000..eacfb29
--- /dev/null
+++ b/DroidFinder/src/com/TwentyCodes/android/DroidFinderFull/TabsAdapter.java
@@ -0,0 +1,127 @@
+/**
+ * TabAdapter.java
+ * @date Mar 2, 2012
+ * @author ricky barrette
+ * @author Twenty Codes, LLC
+ */
+package com.TwentyCodes.android.DroidFinderFull;
+
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TabHost;
+import android.widget.TabWidget;
+
+/**
+ * This is a helper class that implements the management of tabs and all
+ * details of connecting a ViewPager with associated TabHost. It relies on a
+ * trick. Normally a tab host has a simple API for supplying a View or
+ * Intent that each tab will show. This is not sufficient for switching
+ * between pages. So instead we make the content part of the tab host
+ * 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
+ * view to show as the tab content. It listens to changes in tabs, and takes
+ * care of switch to the correct paged in the ViewPager whenever the selected
+ * tab changes.
+ * @author google
+ */
+public class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener {
+ private final Context mContext;
+ private final TabHost mTabHost;
+ private final ViewPager mViewPager;
+ private final ArrayList mTabs = new ArrayList();
+
+ static final class TabInfo {
+ @SuppressWarnings("unused")
+ private final String tag;
+ private final Class> clss;
+ private final Bundle args;
+
+ TabInfo(String _tag, Class> _class, Bundle _args) {
+ tag = _tag;
+ clss = _class;
+ args = _args;
+ }
+ }
+
+ static class DummyTabFactory implements TabHost.TabContentFactory {
+ private final Context mContext;
+
+ public DummyTabFactory(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public View createTabContent(String tag) {
+ View v = new View(mContext);
+ v.setMinimumWidth(0);
+ v.setMinimumHeight(0);
+ return v;
+ }
+ }
+
+ public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager) {
+ super(activity.getSupportFragmentManager());
+ mContext = activity;
+ mTabHost = tabHost;
+ mViewPager = pager;
+ mTabHost.setOnTabChangedListener(this);
+ mViewPager.setAdapter(this);
+ mViewPager.setOnPageChangeListener(this);
+ }
+
+ public void addTab(TabHost.TabSpec tabSpec, Class> clss, Bundle args) {
+ tabSpec.setContent(new DummyTabFactory(mContext));
+ String tag = tabSpec.getTag();
+
+ TabInfo info = new TabInfo(tag, clss, args);
+ mTabs.add(info);
+ mTabHost.addTab(tabSpec);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ TabInfo info = mTabs.get(position);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ }
+
+ @Override
+ public void onTabChanged(String tabId) {
+ int position = mTabHost.getCurrentTab();
+ mViewPager.setCurrentItem(position);
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ // Unfortunately when TabHost changes the current tab, it kindly
+ // also takes care of putting focus on it when not in touch mode.
+ // The jerk.
+ // This hack tries to prevent this from pulling focus out of our
+ // ViewPager.
+ TabWidget widget = mTabHost.getTabWidget();
+ int oldFocusability = widget.getDescendantFocusability();
+ widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ mTabHost.setCurrentTab(position);
+ widget.setDescendantFocusability(oldFocusability);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ }
+}
\ No newline at end of file