summaryrefslogtreecommitdiffstats
path: root/tests/LocationTracker
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2010-02-22 23:42:02 -0500
committerMike Lockwood <lockwood@android.com>2010-02-22 23:57:44 -0500
commitc9d3be3f0e10b62fa6d4899ce827866299d41783 (patch)
treed49433889dae61dfd1c137f139de5df9fe0d549f /tests/LocationTracker
parent2f6a01858bea6c669039b0b1e10aa65fc73c7256 (diff)
downloadframeworks_base-c9d3be3f0e10b62fa6d4899ce827866299d41783.zip
frameworks_base-c9d3be3f0e10b62fa6d4899ce827866299d41783.tar.gz
frameworks_base-c9d3be3f0e10b62fa6d4899ce827866299d41783.tar.bz2
Move LocationTracker test app from vendor/google to frameworks/base/tests
Change-Id: I49f1bfe2081f2c48fcb22b74aa2377857c2bae6d Signed-off-by: Mike Lockwood <lockwood@android.com>
Diffstat (limited to 'tests/LocationTracker')
-rw-r--r--tests/LocationTracker/Android.mk10
-rw-r--r--tests/LocationTracker/AndroidManifest.xml30
-rw-r--r--tests/LocationTracker/res/layout/entrylist_item.xml25
-rw-r--r--tests/LocationTracker/res/menu/menu.xml37
-rw-r--r--tests/LocationTracker/res/values/strings.xml48
-rwxr-xr-xtests/LocationTracker/res/xml/preferences.xml63
-rwxr-xr-xtests/LocationTracker/src/com/google/android/locationtracker/SettingsActivity.java35
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/TrackerActivity.java226
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/TrackerService.java445
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/CSVFormatter.java87
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/DateUtils.java61
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/IFormatter.java27
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/KMLFormatter.java88
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerDataHelper.java173
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerEntry.java253
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerListHelper.java75
-rw-r--r--tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerProvider.java126
17 files changed, 1809 insertions, 0 deletions
diff --git a/tests/LocationTracker/Android.mk b/tests/LocationTracker/Android.mk
new file mode 100644
index 0000000..b142d22
--- /dev/null
+++ b/tests/LocationTracker/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LocationTracker
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/LocationTracker/AndroidManifest.xml b/tests/LocationTracker/AndroidManifest.xml
new file mode 100644
index 0000000..9dbf4ef
--- /dev/null
+++ b/tests/LocationTracker/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.locationtracker">
+
+ <!-- Permissions for the Location Service -->
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+ <!-- Permission for wifi -->
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+ <!-- give the location tracker ability to induce device insomnia -->
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+ <!-- Permission for SD card -->
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application android:label="@string/app_label">
+ <activity android:name="TrackerActivity" android:label="Location Tracker">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <service android:name=".TrackerService" />
+ <activity android:label="@string/settings_menu" android:name="SettingsActivity" />
+ <provider android:name=".data.TrackerProvider"
+ android:authorities="com.google.android.locationtracker" />
+ </application>
+
+</manifest>
diff --git a/tests/LocationTracker/res/layout/entrylist_item.xml b/tests/LocationTracker/res/layout/entrylist_item.xml
new file mode 100644
index 0000000..8187677
--- /dev/null
+++ b/tests/LocationTracker/res/layout/entrylist_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2006-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.
+ */
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/entrylist_item"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+/>
diff --git a/tests/LocationTracker/res/menu/menu.xml b/tests/LocationTracker/res/menu/menu.xml
new file mode 100644
index 0000000..94c589a
--- /dev/null
+++ b/tests/LocationTracker/res/menu/menu.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+* 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.
+*/
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/start_service_menu"
+ android:title="@string/start_service_menu" />
+ <item android:id="@+id/stop_service_menu"
+ android:title="@string/stop_service_menu" />
+ <item android:id="@+id/settings_menu"
+ android:title="@string/settings_menu" />
+ <item android:id="@+id/export_sub_menu"
+ android:title="@string/export_sub_menu">
+ <menu>
+ <item android:id="@+id/export_kml"
+ android:title="@string/export_kml" />
+ <item android:id="@+id/export_csv"
+ android:title="@string/export_csv" />
+ </menu>
+ </item>
+ <item android:title="@string/clear_data"
+ android:id="@+id/clear_data_menu"/>
+</menu>
diff --git a/tests/LocationTracker/res/values/strings.xml b/tests/LocationTracker/res/values/strings.xml
new file mode 100644
index 0000000..bb3ea86
--- /dev/null
+++ b/tests/LocationTracker/res/values/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* 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.
+*/
+-->
+
+<resources>
+ <string name="start_service_menu">Start Service</string>
+ <string name="stop_service_menu">Stop Service</string>
+ <string name="settings_menu">Settings</string>
+ <string name="update_preference">Update frequency</string>
+ <string name="title_mintime_preference">Minimum update time</string>
+ <string name="summary_mintime_preference">The suggested minimum time interval for location updates, in seconds</string>
+ <string name="dialog_title_mintime_preference">Minimum update time</string>
+ <string name="title_mindistance_preference">Minimum distance</string>
+ <string name="summary_mindistance_preference">Minimum distance interval for location updates, in meters</string>
+ <string name="dialog_title_mindistance_preference">Minimum distance</string>
+ <string name="provider_preferences">Location providers</string>
+ <string name="title_network_preference">Network location</string>
+ <string name="summary_network_preference">Listen for updates to network location (Wi-Fi/cellid)</string>
+ <string name="title_gps_preference">GPS location</string>
+ <string name="summary_gps_preference">Listen for updates to GPS location</string>
+ <string name="title_signal_preference">Signal strength</string>
+ <string name="summary_signal_preference">Listen for updates to signal strength</string>
+ <string name="advanced_preferences">Advanced</string>
+ <string name="title_advanced_log_preference">Location debug logging</string>
+ <string name="summary_advanced_preference">Logs detailed location data, only relevant for location/test engineers</string>
+ <string name="app_label">Location Tracker</string>
+ <string name="export_sub_menu">Export\u2026</string>
+ <string name="export_kml">Export As KML</string>
+ <string name="export_csv">Export As CSV</string>
+ <string name="clear_data">Clear data</string>
+ <string name="delete_confirm">All current tracking data will be deleted.</string>
+ <string name="confirm_title">Clear data</string>
+</resources>
diff --git a/tests/LocationTracker/res/xml/preferences.xml b/tests/LocationTracker/res/xml/preferences.xml
new file mode 100755
index 0000000..b57837f
--- /dev/null
+++ b/tests/LocationTracker/res/xml/preferences.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- The Location preferences UI -->
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory android:title="@string/update_preference">
+ <EditTextPreference android:key="mintime_preference"
+ android:defaultValue="0"
+ android:title="@string/title_mintime_preference"
+ android:summary="@string/summary_mintime_preference"
+ android:dialogTitle="@string/dialog_title_mintime_preference" />
+
+ <EditTextPreference android:key="mindistance_preference"
+ android:defaultValue="0"
+ android:title="@string/title_mindistance_preference"
+ android:summary="@string/summary_mindistance_preference"
+ android:dialogTitle="@string/dialog_title_mindistance_preference" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/provider_preferences">
+
+ <CheckBoxPreference android:key="network_preference"
+ android:defaultValue="true"
+ android:title="@string/title_network_preference"
+ android:summary="@string/summary_network_preference" />
+
+ <CheckBoxPreference android:key="gps_preference"
+ android:defaultValue="true"
+ android:title="@string/title_gps_preference"
+ android:summary="@string/summary_gps_preference" />
+
+ <CheckBoxPreference android:key="signal_preference"
+ android:defaultValue="false"
+ android:title="@string/title_signal_preference"
+ android:summary="@string/summary_signal_preference" />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/advanced_preferences">
+
+ <CheckBoxPreference android:key="advanced_log_preference"
+ android:defaultValue="false"
+ android:title="@string/title_advanced_log_preference"
+ android:summary="@string/summary_advanced_preference" />
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/SettingsActivity.java b/tests/LocationTracker/src/com/google/android/locationtracker/SettingsActivity.java
new file mode 100755
index 0000000..edfe65a
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/SettingsActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Settings preference screen for location tracker
+ */
+public class SettingsActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/TrackerActivity.java b/tests/LocationTracker/src/com/google/android/locationtracker/TrackerActivity.java
new file mode 100644
index 0000000..a96c6bb
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/TrackerActivity.java
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker;
+
+import com.google.android.locationtracker.data.DateUtils;
+import com.google.android.locationtracker.data.TrackerDataHelper;
+import com.google.android.locationtracker.data.TrackerListHelper;
+
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Activity for location tracker service
+ *
+ * Contains facilities for starting and
+ * stopping location tracker service, as well as displaying the current location
+ * data
+ *
+ * Future enhancements:
+ * - export data as dB
+ * - enable/disable "start service" and "stop service" menu items as
+ * appropriate
+ */
+public class TrackerActivity extends ListActivity {
+
+ static final String LOG_TAG = "LocationTracker";
+
+ private TrackerListHelper mDataHelper;
+
+ /**
+ * Retrieves and displays the currently logged location data from file
+ *
+ * @param icicle
+ */
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mDataHelper = new TrackerListHelper(this);
+ mDataHelper.bindListUI(R.layout.entrylist_item);
+ }
+
+ /**
+ * Builds the menu
+ *
+ * @param menu - menu to add items to
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater menuInflater = getMenuInflater();
+ menuInflater.inflate(R.menu.menu, menu);
+ return true;
+ }
+
+ /**
+ * Handles menu item selection
+ *
+ * @param item - the selected menu item
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.start_service_menu: {
+ startService(new Intent(TrackerActivity.this,
+ TrackerService.class));
+ break;
+ }
+ case R.id.stop_service_menu: {
+ stopService(new Intent(TrackerActivity.this,
+ TrackerService.class));
+ break;
+ }
+ case R.id.settings_menu: {
+ launchSettings();
+ break;
+ }
+ case R.id.export_kml: {
+ exportKML();
+ break;
+ }
+ case R.id.export_csv: {
+ exportCSV();
+ break;
+ }
+ case R.id.clear_data_menu: {
+ clearData();
+ break;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void clearData() {
+ Runnable clearAction = new Runnable() {
+ public void run() {
+ TrackerDataHelper helper =
+ new TrackerDataHelper(TrackerActivity.this);
+ helper.deleteAll();
+ }
+ };
+ showConfirm(R.string.delete_confirm, clearAction);
+ }
+
+ private void showConfirm(int textId, final Runnable onConfirmAction) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+ dialogBuilder.setTitle(R.string.confirm_title);
+ dialogBuilder.setMessage(textId);
+ dialogBuilder.setPositiveButton(android.R.string.ok,
+ new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ onConfirmAction.run();
+ }
+ });
+ dialogBuilder.setNegativeButton(android.R.string.cancel,
+ new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // do nothing
+ }
+ });
+ dialogBuilder.show();
+ }
+
+ private void exportCSV() {
+ String exportFileName = getUniqueFileName("csv");
+ exportFile(null, exportFileName, new TrackerDataHelper(this,
+ TrackerDataHelper.CSV_FORMATTER));
+ }
+
+ private void exportKML() {
+ String exportFileName = getUniqueFileName(
+ LocationManager.NETWORK_PROVIDER + ".kml");
+ exportFile(LocationManager.NETWORK_PROVIDER, exportFileName,
+ new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+ exportFileName = getUniqueFileName(
+ LocationManager.GPS_PROVIDER + ".kml");
+ exportFile(LocationManager.GPS_PROVIDER, exportFileName,
+ new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+ }
+
+ private void exportFile(String tagFilter,
+ String exportFileName,
+ TrackerDataHelper trackerData) {
+ BufferedWriter exportWriter = null;
+ Cursor cursor = trackerData.query(tagFilter);
+ try {
+ exportWriter = new BufferedWriter(new FileWriter(exportFileName));
+ exportWriter.write(trackerData.getOutputHeader());
+
+ String line = null;
+
+ while ((line = trackerData.getNextOutput(cursor)) != null) {
+ exportWriter.write(line);
+ }
+ exportWriter.write(trackerData.getOutputFooter());
+ Toast.makeText(this, "Successfully exported data to " +
+ exportFileName, Toast.LENGTH_SHORT).show();
+
+ } catch (IOException e) {
+ Toast.makeText(this, "Error exporting file: " +
+ e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
+
+ Log.e(LOG_TAG, "Error exporting file", e);
+ } finally {
+ closeWriter(exportWriter);
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private void closeWriter(Writer exportWriter) {
+ if (exportWriter != null) {
+ try {
+ exportWriter.close();
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "error closing file", e);
+ }
+ }
+ }
+
+ private String getUniqueFileName(String ext) {
+ File dir = new File("/sdcard/locationtracker");
+ if (!dir.exists()) {
+ dir.mkdir();
+ }
+ return "/sdcard/locationtracker/tracking-" +
+ DateUtils.getCurrentTimestamp() + "." + ext;
+ }
+
+ private void launchSettings() {
+ Intent settingsIntent = new Intent();
+ settingsIntent.setClass(this, SettingsActivity.class);
+ startActivity(settingsIntent);
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/TrackerService.java b/tests/LocationTracker/src/com/google/android/locationtracker/TrackerService.java
new file mode 100644
index 0000000..e7fdc494
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/TrackerService.java
@@ -0,0 +1,445 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker;
+
+import com.google.android.locationtracker.data.TrackerDataHelper;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Location Tracking service
+ *
+ * Records location updates for all registered location providers, and cell
+ * location updates
+ */
+public class TrackerService extends Service {
+
+ private List<LocationTrackingListener> mListeners;
+
+ private static final String LOG_TAG = TrackerActivity.LOG_TAG;
+
+ // controls which location providers to track
+ private Set<String> mTrackedProviders;
+
+ private TrackerDataHelper mTrackerData;
+
+ private TelephonyManager mTelephonyManager;
+ private Location mNetworkLocation;
+
+ // Handlers and Receivers for phone and network state
+ private NetworkStateBroadcastReceiver mNetwork;
+ private static final String CELL_PROVIDER_TAG = "cell";
+ // signal strength updates
+ private static final String SIGNAL_PROVIDER_TAG = "signal";
+ private static final String WIFI_PROVIDER_TAG = "wifi";
+ // tracking tag for data connectivity issues
+ private static final String DATA_CONN_PROVIDER_TAG = "data";
+
+ // preference constants
+ private static final String MIN_TIME_PREF = "mintime_preference";
+ private static final String MIN_DIS_PREF = "mindistance_preference";
+ private static final String GPS_PREF = "gps_preference";
+ private static final String NETWORK_PREF = "network_preference";
+ private static final String SIGNAL_PREF = "signal_preference";
+ private static final String DEBUG_PREF = "advanced_log_preference";
+
+ private PreferenceListener mPrefListener;
+
+ public TrackerService() {
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // ignore - nothing to do
+ return null;
+ }
+
+ /**
+ * registers location listeners
+ *
+ * @param intent
+ * @param startId
+ */
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ mNetworkLocation = null;
+
+ initLocationListeners();
+ Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT);
+ }
+
+ private synchronized void initLocationListeners() {
+ mTrackerData = new TrackerDataHelper(this);
+ LocationManager lm = getLocationManager();
+
+ mTrackedProviders = getTrackedProviders();
+
+ List<String> locationProviders = lm.getAllProviders();
+ mListeners = new ArrayList<LocationTrackingListener>(
+ locationProviders.size());
+
+ long minUpdateTime = getLocationUpdateTime();
+ float minDistance = getLocationMinDistance();
+
+ for (String providerName : locationProviders) {
+ if (mTrackedProviders.contains(providerName)) {
+ Log.d(LOG_TAG, "Adding location listener for provider " +
+ providerName);
+ if (doDebugLogging()) {
+ mTrackerData.writeEntry("init", String.format(
+ "start listening to %s : %d ms; %f meters",
+ providerName, minUpdateTime, minDistance));
+ }
+ LocationTrackingListener listener =
+ new LocationTrackingListener();
+ lm.requestLocationUpdates(providerName, minUpdateTime,
+ minDistance, listener);
+ mListeners.add(listener);
+ }
+ }
+ mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
+
+ if (doDebugLogging()) {
+ // register for cell location updates
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
+
+ // Register for Network (Wifi or Mobile) updates
+ mNetwork = new NetworkStateBroadcastReceiver();
+ IntentFilter mIntentFilter;
+ mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ Log.d(LOG_TAG, "registering receiver");
+ registerReceiver(mNetwork, mIntentFilter);
+ }
+
+ if (trackSignalStrength()) {
+ mTelephonyManager.listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+ }
+
+ // register for preference changes, so we can restart listeners on
+ // pref changes
+ mPrefListener = new PreferenceListener();
+ getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener);
+ }
+
+ private Set<String> getTrackedProviders() {
+ Set<String> providerSet = new HashSet<String>();
+
+ if (trackGPS()) {
+ providerSet.add(LocationManager.GPS_PROVIDER);
+ }
+ if (trackNetwork()) {
+ providerSet.add(LocationManager.NETWORK_PROVIDER);
+ }
+ return providerSet;
+ }
+
+ private SharedPreferences getPreferences() {
+ return PreferenceManager.getDefaultSharedPreferences(this);
+ }
+
+ private boolean trackNetwork() {
+ return getPreferences().getBoolean(NETWORK_PREF, true);
+ }
+
+ private boolean trackGPS() {
+ return getPreferences().getBoolean(GPS_PREF, true);
+ }
+
+ private boolean doDebugLogging() {
+ return getPreferences().getBoolean(DEBUG_PREF, true);
+ }
+
+ private boolean trackSignalStrength() {
+ return getPreferences().getBoolean(SIGNAL_PREF, true);
+ }
+
+ private float getLocationMinDistance() {
+ try {
+ String disString = getPreferences().getString(MIN_DIS_PREF, "0");
+ return Float.parseFloat(disString);
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Invalid preference for location min distance", e);
+ }
+ return 0;
+ }
+
+ private long getLocationUpdateTime() {
+ try {
+ String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
+ long secondsTime = Long.valueOf(timeString);
+ return secondsTime * 1000;
+ }
+ catch (NumberFormatException e) {
+ Log.e(LOG_TAG, "Invalid preference for location min time", e);
+ }
+ return 0;
+ }
+
+ /**
+ * Shuts down this service
+ */
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(LOG_TAG, "Removing location listeners");
+ stopListeners();
+ Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT);
+ }
+
+ /**
+ * De-registers all location listeners, closes persistent storage
+ */
+ protected synchronized void stopListeners() {
+ LocationManager lm = getLocationManager();
+ if (mListeners != null) {
+ for (LocationTrackingListener listener : mListeners) {
+ lm.removeUpdates(listener);
+ }
+ mListeners.clear();
+ }
+ mListeners = null;
+
+ // stop cell state listener
+ if (mTelephonyManager != null) {
+ mTelephonyManager.listen(mPhoneStateListener, 0);
+ }
+
+ // stop network/wifi listener
+ if (mNetwork != null) {
+ unregisterReceiver(mNetwork);
+ }
+ mNetwork = null;
+
+ mTrackerData = null;
+ if (mPrefListener != null) {
+ getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener);
+ mPrefListener = null;
+ }
+ }
+
+ private LocationManager getLocationManager() {
+ return (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ }
+
+ /**
+ * Determine the current distance from given location to the last
+ * approximated network location
+ *
+ * @param location - new location
+ *
+ * @return float distance in meters
+ */
+ private synchronized float getDistanceFromNetwork(Location location) {
+ float value = 0;
+ if (mNetworkLocation != null) {
+ value = location.distanceTo(mNetworkLocation);
+ }
+ if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+ mNetworkLocation = location;
+ }
+ return value;
+ }
+
+ private class LocationTrackingListener implements LocationListener {
+
+ /**
+ * Writes details of location update to tracking file, including
+ * recording the distance between this location update and the last
+ * network location update
+ *
+ * @param location - new location
+ */
+ public void onLocationChanged(Location location) {
+ if (location == null) {
+ return;
+ }
+ float distance = getDistanceFromNetwork(location);
+ mTrackerData.writeEntry(location, distance);
+ }
+
+ /**
+ * Writes update to tracking file
+ *
+ * @param provider - name of disabled provider
+ */
+ public void onProviderDisabled(String provider) {
+ if (doDebugLogging()) {
+ mTrackerData.writeEntry(provider, "provider disabled");
+ }
+ }
+
+ /**
+ * Writes update to tracking file
+ *
+ * @param provider - name of enabled provider
+ */
+ public void onProviderEnabled(String provider) {
+ if (doDebugLogging()) {
+ mTrackerData.writeEntry(provider, "provider enabled");
+ }
+ }
+
+ /**
+ * Writes update to tracking file
+ *
+ * @param provider - name of provider whose status changed
+ * @param status - new status
+ * @param extras - optional set of extra status messages
+ */
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ if (doDebugLogging()) {
+ mTrackerData.writeEntry(provider, "status change: " + status);
+ }
+ }
+ }
+
+ PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCellLocationChanged(CellLocation location) {
+ try {
+ if (location instanceof GsmCellLocation) {
+ GsmCellLocation cellLocation = (GsmCellLocation)location;
+ String updateMsg = "cid=" + cellLocation.getCid() +
+ ", lac=" + cellLocation.getLac();
+ mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+ } else if (location instanceof CdmaCellLocation) {
+ CdmaCellLocation cellLocation = (CdmaCellLocation)location;
+ String updateMsg = "BID=" + cellLocation.getBaseStationId() +
+ ", SID=" + cellLocation.getSystemId() +
+ ", NID=" + cellLocation.getNetworkId() +
+ ", lat=" + cellLocation.getBaseStationLatitude() +
+ ", long=" + cellLocation.getBaseStationLongitude() +
+ ", SID=" + cellLocation.getSystemId() +
+ ", NID=" + cellLocation.getNetworkId();
+ mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e);
+ }
+ }
+
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+ String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm();
+ mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+ } else if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+ String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength();
+ mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+ }
+ }
+ };
+
+ /**
+ * Listener + recorder for mobile or wifi updates
+ */
+ private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+
+ if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ WifiManager wifiManager =
+ (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ List<ScanResult> wifiScanResults = wifiManager.getScanResults();
+ String updateMsg = "num scan results=" +
+ (wifiScanResults == null ? "0" : wifiScanResults.size());
+ mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg);
+
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ String updateMsg;
+ boolean noConnectivity =
+ intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ if (noConnectivity) {
+ updateMsg = "no connectivity";
+ }
+ else {
+ updateMsg = "connection available";
+ }
+ mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg);
+
+ } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN);
+
+ String stateString = "unknown";
+ switch (state) {
+ case WifiManager.WIFI_STATE_DISABLED:
+ stateString = "disabled";
+ break;
+ case WifiManager.WIFI_STATE_DISABLING:
+ stateString = "disabling";
+ break;
+ case WifiManager.WIFI_STATE_ENABLED:
+ stateString = "enabled";
+ break;
+ case WifiManager.WIFI_STATE_ENABLING:
+ stateString = "enabling";
+ break;
+ }
+ mTrackerData.writeEntry(WIFI_PROVIDER_TAG,
+ "state = " + stateString);
+ }
+ }
+ }
+
+ private class PreferenceListener implements OnSharedPreferenceChangeListener {
+
+ public void onSharedPreferenceChanged(
+ SharedPreferences sharedPreferences, String key) {
+ Log.d(LOG_TAG, "restarting listeners due to preference change");
+ synchronized (TrackerService.this) {
+ stopListeners();
+ initLocationListeners();
+ }
+ }
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/CSVFormatter.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/CSVFormatter.java
new file mode 100644
index 0000000..cd48db4
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/CSVFormatter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import com.google.android.locationtracker.data.TrackerEntry.EntryType;
+
+/**
+ * Formats tracker data as CSV output
+ */
+class CSVFormatter implements IFormatter {
+
+ private static final String DELIMITER = ", ";
+
+ public String getHeader() {
+ StringBuilder csvBuilder = new StringBuilder();
+ for (String col : TrackerEntry.ATTRIBUTES) {
+ // skip type and id column
+ if (!TrackerEntry.ENTRY_TYPE.equals(col) &&
+ !TrackerEntry.ID_COL.equals(col)) {
+ csvBuilder.append(col);
+ csvBuilder.append(DELIMITER);
+ }
+ }
+ csvBuilder.append("\n");
+ return csvBuilder.toString();
+ }
+
+ public String getOutput(TrackerEntry entry) {
+ StringBuilder rowOutput = new StringBuilder();
+ // these must match order of columns added in getHeader
+ rowOutput.append(entry.getTimestamp());
+ rowOutput.append(DELIMITER);
+ rowOutput.append(entry.getTag());
+ rowOutput.append(DELIMITER);
+ //rowOutput.append(entry.getType());
+ //rowOutput.append(DELIMITER);
+ if (entry.getType() == EntryType.LOCATION_TYPE) {
+ if (entry.getLocation().hasAccuracy()) {
+ rowOutput.append(entry.getLocation().getAccuracy());
+ }
+ rowOutput.append(DELIMITER);
+ rowOutput.append(entry.getLocation().getLatitude());
+ rowOutput.append(DELIMITER);
+ rowOutput.append(entry.getLocation().getLongitude());
+ rowOutput.append(DELIMITER);
+ if (entry.getLocation().hasAltitude()) {
+ rowOutput.append(entry.getLocation().getAltitude());
+ }
+ rowOutput.append(DELIMITER);
+ if (entry.getLocation().hasSpeed()) {
+ rowOutput.append(entry.getLocation().getSpeed());
+ }
+ rowOutput.append(DELIMITER);
+ if (entry.getLocation().hasBearing()) {
+ rowOutput.append(entry.getLocation().getBearing());
+ }
+ rowOutput.append(DELIMITER);
+ rowOutput.append(entry.getDistFromNetLocation());
+ rowOutput.append(DELIMITER);
+ rowOutput.append(DateUtils.getKMLTimestamp(entry.getLocation()
+ .getTime()));
+ rowOutput.append(DELIMITER);
+ }
+ rowOutput.append(entry.getLogMsg());
+ rowOutput.append("\n");
+ return rowOutput.toString();
+ }
+
+ public String getFooter() {
+ // not needed, return empty string
+ return "";
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/DateUtils.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/DateUtils.java
new file mode 100644
index 0000000..78d6705
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/DateUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Provides formatting date as string utilities
+ */
+public class DateUtils {
+
+ private DateUtils() {
+
+ }
+
+ /**
+ * Returns timestamp given by param in KML format ie yyyy-mm-ddThh:mm:ssZ,
+ * where T is the separator between the date and the time and the time zone
+ * is Z (for UTC)
+ *
+ * @return KML timestamp as String
+ */
+ public static String getKMLTimestamp(long when) {
+ TimeZone tz = TimeZone.getTimeZone("GMT");
+ Calendar c = Calendar.getInstance(tz);
+ c.setTimeInMillis(when);
+ return String.format("%tY-%tm-%tdT%tH:%tM:%tSZ", c, c, c, c, c, c);
+ }
+
+ /**
+ * Helper version of getKMLTimestamp, that returns timestamp for current
+ * time
+ */
+ public static String getCurrentKMLTimestamp() {
+ return getKMLTimestamp(System.currentTimeMillis());
+ }
+
+ /**
+ * Returns timestamp in following format: yyyy-mm-dd-hh-mm-ss
+ */
+ public static String getCurrentTimestamp() {
+ Calendar c = Calendar.getInstance();
+ c.setTimeInMillis(System.currentTimeMillis());
+ return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c);
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/IFormatter.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/IFormatter.java
new file mode 100644
index 0000000..d921249
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/IFormatter.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+/**
+ * interface for formatting tracker data output
+ */
+interface IFormatter {
+
+ String getHeader();
+ String getOutput(TrackerEntry entry);
+ String getFooter();
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/KMLFormatter.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/KMLFormatter.java
new file mode 100644
index 0000000..a49827c
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/KMLFormatter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import com.google.android.locationtracker.data.TrackerEntry.EntryType;
+
+import android.location.Location;
+
+/**
+ * Formats tracker data as KML output
+ */
+class KMLFormatter implements IFormatter {
+
+ public String getHeader() {
+ LineBuilder builder = new LineBuilder();
+ builder.addLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ builder.addLine("<kml xmlns=\"http://earth.google.com/kml/2.2\">");
+ builder.addLine("<Document>");
+ return builder.toString();
+ }
+
+ public String getFooter() {
+ LineBuilder builder = new LineBuilder();
+ builder.addLine("</Document>");
+ builder.addLine("</kml>");
+ return builder.toString();
+ }
+
+ public String getOutput(TrackerEntry entry) {
+ LineBuilder builder = new LineBuilder();
+
+ if (entry.getType() == EntryType.LOCATION_TYPE) {
+
+ Location loc = entry.getLocation();
+ builder.addLine("<Placemark>");
+ builder.addLine("<description>");
+ builder.addLine("accuracy = " + loc.getAccuracy());
+ builder.addLine("distance from last network location = "
+ + entry.getDistFromNetLocation());
+ builder.addLine("</description>");
+ builder.addLine("<TimeStamp>");
+ builder.addLine("<when>" + entry.getTimestamp() + "</when>");
+ builder.addLine("</TimeStamp>");
+ builder.addLine("<Point>");
+ builder.addLine("<coordinates>");
+ builder.addLine(loc.getLongitude() + "," + loc.getLatitude() + ","
+ + loc.getAltitude());
+ builder.addLine("</coordinates>");
+ builder.addLine("</Point>");
+ builder.addLine("</Placemark>");
+ }
+ return builder.toString();
+ }
+
+ private static class LineBuilder {
+ private StringBuilder mBuilder;
+
+ public LineBuilder() {
+ mBuilder = new StringBuilder();
+ }
+
+ public void addLine(String line) {
+ mBuilder.append(line);
+ mBuilder.append("\n");
+ }
+
+ @Override
+ public String toString() {
+ return mBuilder.toString();
+ }
+
+ }
+
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerDataHelper.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerDataHelper.java
new file mode 100644
index 0000000..cdcc2b3
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerDataHelper.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.location.Location;
+
+/**
+ * Helper class for writing and retrieving data using the TrackerProvider
+ * content provider
+ *
+ */
+public class TrackerDataHelper {
+
+ private Context mContext;
+ /** formats data output */
+ protected IFormatter mFormatter;
+
+ /** formats output as Comma separated value CSV file */
+ public static final IFormatter CSV_FORMATTER = new CSVFormatter();
+ /** formats output as KML file */
+ public static final IFormatter KML_FORMATTER = new KMLFormatter();
+ /** provides no formatting */
+ public static final IFormatter NO_FORMATTER = new IFormatter() {
+ public String getFooter() {
+ return "";
+ }
+
+ public String getHeader() {
+ return "";
+ }
+
+ public String getOutput(TrackerEntry entry) {
+ return "";
+ }
+ };
+
+ /**
+ * Creates instance
+ *
+ * @param context - content context
+ * @param formatter - formats the output from the get*Output* methods
+ */
+ public TrackerDataHelper(Context context, IFormatter formatter) {
+ mContext = context;
+ mFormatter = formatter;
+ }
+
+ /**
+ * Creates a instance with no output formatting capabilities. Useful for
+ * clients that require write-only access
+ */
+ public TrackerDataHelper(Context context) {
+ this(context, NO_FORMATTER);
+ }
+
+ /**
+ * insert given TrackerEntry into content provider
+ */
+ void writeEntry(TrackerEntry entry) {
+ mContext.getContentResolver().insert(TrackerProvider.CONTENT_URI,
+ entry.getAsContentValues());
+ }
+
+ /**
+ * insert given location into tracker data
+ */
+ public void writeEntry(Location loc, float distFromNetLoc) {
+ writeEntry(TrackerEntry.createEntry(loc, distFromNetLoc));
+ }
+
+ /**
+ * insert given log message into tracker data
+ */
+ public void writeEntry(String tag, String logMsg) {
+ writeEntry(TrackerEntry.createEntry(tag, logMsg));
+ }
+
+ /**
+ * Deletes all tracker entries
+ */
+ public void deleteAll() {
+ mContext.getContentResolver().delete(TrackerProvider.CONTENT_URI, null,
+ null);
+ }
+
+ /**
+ * Query tracker data, filtering by given tag
+ *
+ * @param tag
+ * @return Cursor to data
+ */
+ public Cursor query(String tag, int limit) {
+ String selection = (tag == null ? null : TrackerEntry.TAG + "=?");
+ String[] selectionArgs = (tag == null ? null : new String[] {tag});
+ Cursor cursor = mContext.getContentResolver().query(
+ TrackerProvider.CONTENT_URI, TrackerEntry.ATTRIBUTES,
+ selection, selectionArgs, null);
+ if (cursor == null) {
+ return cursor;
+ }
+ int pos = (cursor.getCount() < limit ? 0 : cursor.getCount() - limit);
+ cursor.moveToPosition(pos);
+ return cursor;
+ }
+
+ /**
+ * Retrieves a cursor that starts at the last limit rows
+ *
+ * @param limit
+ * @return a cursor, null if bad things happened
+ */
+ public Cursor query(int limit) {
+ return query(null, limit);
+ }
+
+ /**
+ * Query tracker data, filtering by given tag. mo limit to number of rows
+ * returned
+ *
+ * @param tag
+ * @return Cursor to data
+ */
+ public Cursor query(String tag) {
+ return query(tag, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the output header particular to the associated formatter
+ */
+ public String getOutputHeader() {
+ return mFormatter.getHeader();
+ }
+
+ /**
+ * Returns the output footer particular to the associated formatter
+ */
+ public String getOutputFooter() {
+ return mFormatter.getFooter();
+ }
+
+ /**
+ * Helper method which converts row referenced by given cursor to a string
+ * output
+ *
+ * @param cursor
+ * @return CharSequence output, null if given cursor is invalid or no more
+ * data
+ */
+ public String getNextOutput(Cursor cursor) {
+ if (cursor == null || cursor.isAfterLast()) {
+ return null;
+ }
+ String output = mFormatter.getOutput(TrackerEntry.createEntry(cursor));
+ cursor.moveToNext();
+ return output;
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerEntry.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerEntry.java
new file mode 100644
index 0000000..91e6dcc
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerEntry.java
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.location.Location;
+
+
+/**
+ * Class that holds a tracker entry. An entry can be either a valid location, or
+ * a simple log msg
+ *
+ * It provides a concrete data structure to represent data stored in the
+ * TrackerProvider
+ */
+class TrackerEntry {
+
+ static final String TIMESTAMP = "Timestamp";
+ static final String TAG = "Tag";
+ static final String ENTRY_TYPE = "Type";
+
+ private Location mLocation;
+ private float mDistFromNetLocation;
+ private String mLogMsg;
+
+ static final String ID_COL = "_id";
+ static final String ACCURACY = "Accuracy";
+ static final String LATITUDE = "Latitude";
+ static final String LONGITUDE = "Longitude";
+ static final String ALTITUDE = "Altitude";
+ static final String SPEED = "Speed";
+ static final String BEARING = "Bearing";
+ static final String DIST_NET_LOCATION = "DistFromNetLocation";
+ static final String LOC_TIME = "LocationTime";
+ static final String DEBUG_INFO = "DebugInfo";
+
+ static final String STRING_DATA = "STRING";
+ static final String INT_DATA = "INTEGER";
+ static final String REAL_DATA = "REAL";
+ static final String BLOB_DATA = "BLOB";
+
+ static final String[] ATTRIBUTES = {
+ ID_COL, TIMESTAMP, TAG, ENTRY_TYPE, ACCURACY, LATITUDE, LONGITUDE,
+ ALTITUDE, SPEED, BEARING, DIST_NET_LOCATION, LOC_TIME, DEBUG_INFO};
+ static final String[] ATTRIBUTES_DATA_TYPE = {
+ INT_DATA + " PRIMARY KEY", STRING_DATA, STRING_DATA, STRING_DATA,
+ REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA,
+ REAL_DATA, INT_DATA, STRING_DATA};
+
+ // location extra keys used to retrieve debug info
+ private static final String NETWORK_LOCATION_SOURCE_KEY =
+ "networkLocationSource";
+ private static final String NETWORK_LOCATION_TYPE_KEY =
+ "networkLocationType";
+ private static final String[] LOCATION_DEBUG_KEYS = {
+ NETWORK_LOCATION_SOURCE_KEY, NETWORK_LOCATION_TYPE_KEY};
+
+ enum EntryType {
+ LOCATION_TYPE, LOG_TYPE
+ }
+
+ private String mTimestamp;
+ private String mTag;
+ private EntryType mType;
+
+ private TrackerEntry(String tag, EntryType type) {
+ mType = type;
+ mTag = tag;
+ mLocation = null;
+ }
+
+ private TrackerEntry(Location loc) {
+ this(loc.getProvider(), EntryType.LOCATION_TYPE);
+ mLocation = new Location(loc);
+ }
+
+ /**
+ * Creates a TrackerEntry from a Location
+ */
+ static TrackerEntry createEntry(Location loc, float distFromNetLocation) {
+ TrackerEntry entry = new TrackerEntry(loc);
+
+ String timestampVal = DateUtils.getCurrentKMLTimestamp();
+ entry.setTimestamp(timestampVal);
+ entry.setDistFromNetLocation(distFromNetLocation);
+ return entry;
+ }
+
+ /**
+ * Creates a TrackerEntry from a log msg
+ */
+ static TrackerEntry createEntry(String tag, String msg) {
+ TrackerEntry entry = new TrackerEntry(tag, EntryType.LOG_TYPE);
+ String timestampVal = DateUtils.getCurrentKMLTimestamp();
+ entry.setTimestamp(timestampVal);
+ entry.setLogMsg(msg);
+ return entry;
+ }
+
+ private void setTimestamp(String timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ EntryType getType() {
+ return mType;
+ }
+
+ private void setDistFromNetLocation(float distFromNetLocation) {
+ mDistFromNetLocation = distFromNetLocation;
+ }
+
+ private void setLogMsg(String msg) {
+ mLogMsg = msg;
+ }
+
+ private void setLocation(Location location) {
+ mLocation = location;
+ }
+
+ String getTimestamp() {
+ return mTimestamp;
+ }
+
+ String getTag() {
+ return mTag;
+ }
+
+ Location getLocation() {
+ return mLocation;
+ }
+
+ String getLogMsg() {
+ return mLogMsg;
+ }
+
+ float getDistFromNetLocation() {
+ return mDistFromNetLocation;
+ }
+
+ static void buildCreationString(StringBuilder builder) {
+ if (ATTRIBUTES.length != ATTRIBUTES_DATA_TYPE.length) {
+ throw new IllegalArgumentException(
+ "Attribute length does not match data type length");
+ }
+ for (int i = 0; i < ATTRIBUTES_DATA_TYPE.length; i++) {
+ if (i != 0) {
+ builder.append(", ");
+ }
+ builder.append(String.format("%s %s", ATTRIBUTES[i],
+ ATTRIBUTES_DATA_TYPE[i]));
+ }
+ }
+
+ ContentValues getAsContentValues() {
+ ContentValues cValues = new ContentValues(ATTRIBUTES.length);
+ cValues.put(TIMESTAMP, mTimestamp);
+ cValues.put(TAG, mTag);
+ cValues.put(ENTRY_TYPE, mType.toString());
+ if (mType == EntryType.LOCATION_TYPE) {
+ cValues.put(LATITUDE, mLocation.getLatitude());
+ cValues.put(LONGITUDE, mLocation.getLongitude());
+ if (mLocation.hasAccuracy()) {
+ cValues.put(ACCURACY, mLocation.getAccuracy());
+ }
+ if (mLocation.hasAltitude()) {
+ cValues.put(ALTITUDE, mLocation.getAltitude());
+ }
+ if (mLocation.hasSpeed()) {
+ cValues.put(SPEED, mLocation.getSpeed());
+ }
+ if (mLocation.hasBearing()) {
+ cValues.put(BEARING, mLocation.getBearing());
+ }
+ cValues.put(DIST_NET_LOCATION, mDistFromNetLocation);
+ cValues.put(LOC_TIME, mLocation.getTime());
+ StringBuilder debugBuilder = new StringBuilder("");
+ if (mLocation.getExtras() != null) {
+ for (String key : LOCATION_DEBUG_KEYS) {
+ Object val = mLocation.getExtras().get(key);
+ if (val != null) {
+ debugBuilder.append(String.format("%s=%s; ", key, val
+ .toString()));
+ }
+ }
+ }
+ cValues.put(DEBUG_INFO, debugBuilder.toString());
+ } else {
+ cValues.put(DEBUG_INFO, mLogMsg);
+ }
+ return cValues;
+ }
+
+ static TrackerEntry createEntry(Cursor cursor) {
+ String timestamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP));
+ String tag = cursor.getString(cursor.getColumnIndex(TAG));
+ String sType = cursor.getString(cursor.getColumnIndex(ENTRY_TYPE));
+ TrackerEntry entry = new TrackerEntry(tag, EntryType.valueOf(sType));
+ entry.setTimestamp(timestamp);
+ if (entry.getType() == EntryType.LOCATION_TYPE) {
+ Location location = new Location(tag);
+ location.setLatitude(cursor.getFloat(cursor
+ .getColumnIndexOrThrow(LATITUDE)));
+ location.setLongitude(cursor.getFloat(cursor
+ .getColumnIndexOrThrow(LONGITUDE)));
+
+ Float accuracy = getNullableFloat(cursor, ACCURACY);
+ if (accuracy != null) {
+ location.setAccuracy(accuracy);
+ }
+ Float altitude = getNullableFloat(cursor, ALTITUDE);
+ if (altitude != null) {
+ location.setAltitude(altitude);
+ }
+ Float bearing = getNullableFloat(cursor, BEARING);
+ if (bearing != null) {
+ location.setBearing(bearing);
+ }
+ Float speed = getNullableFloat(cursor, SPEED);
+ if (speed != null) {
+ location.setSpeed(speed);
+ }
+ location.setTime(cursor.getLong(cursor.getColumnIndex(LOC_TIME)));
+ entry.setLocation(location);
+ }
+ entry.setLogMsg(cursor.getString(cursor.getColumnIndex(DEBUG_INFO)));
+
+ return entry;
+ }
+
+ private static Float getNullableFloat(Cursor cursor, String colName) {
+ Float retValue = null;
+ int colIndex = cursor.getColumnIndexOrThrow(colName);
+ if (!cursor.isNull(colIndex)) {
+ retValue = cursor.getFloat(colIndex);
+ }
+ return retValue;
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerListHelper.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerListHelper.java
new file mode 100644
index 0000000..e292b45b
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerListHelper.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package com.google.android.locationtracker.data;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.widget.ResourceCursorAdapter;
+import android.widget.TextView;
+
+import com.google.android.locationtracker.R;
+
+/**
+ * Used to bind Tracker data to a list view UI
+ */
+public class TrackerListHelper extends TrackerDataHelper {
+
+ private ListActivity mActivity;
+
+ // sort entries by most recent first
+ private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";
+
+ public TrackerListHelper(ListActivity activity) {
+ super(activity, TrackerDataHelper.CSV_FORMATTER);
+ mActivity = activity;
+ }
+
+ /**
+ * Helper method for binding the list activities UI to the tracker data
+ * Tracker data will be sorted in most-recent first order
+ * Will enable automatic UI changes as tracker data changes
+ *
+ * @param layout - layout to populate data
+ */
+ public void bindListUI(int layout) {
+ Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,
+ TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);
+ // Used to map tracker entries from the database to views
+ TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);
+ mActivity.setListAdapter(adapter);
+ cursor.setNotificationUri(mActivity.getContentResolver(),
+ TrackerProvider.CONTENT_URI);
+
+ }
+
+ private class TrackerAdapter extends ResourceCursorAdapter {
+
+ public TrackerAdapter(Context context, int layout, Cursor c) {
+ super(context, layout, c);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ final TextView v = (TextView) view
+ .findViewById(R.id.entrylist_item);
+ String rowText = mFormatter.getOutput(TrackerEntry
+ .createEntry(cursor));
+ v.setText(rowText);
+ }
+ }
+}
diff --git a/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerProvider.java b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerProvider.java
new file mode 100644
index 0000000..052d813
--- /dev/null
+++ b/tests/LocationTracker/src/com/google/android/locationtracker/data/TrackerProvider.java
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+package com.google.android.locationtracker.data;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * Content provider for location tracking.
+ *
+ * It is recommended to use the TrackerDataHelper class to access this data
+ * rather than this class directly
+ */
+public class TrackerProvider extends ContentProvider {
+
+ public static final Uri CONTENT_URI = Uri
+ .parse("content://com.google.android.locationtracker");
+
+ private static final String DB_NAME = "tracking.db";
+ private static final String TABLE_NAME = "tracking";
+ private static final int DB_VERSION = 1;
+
+ private static final String LOG_TAG = "TrackerProvider";
+
+ /**
+ * This class helps open, create, and upgrade the database file.
+ */
+ private static class DatabaseHelper extends SQLiteOpenHelper {
+
+ DatabaseHelper(Context context) {
+ super(context, DB_NAME, null, DB_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ StringBuilder queryBuilder = new StringBuilder();
+ queryBuilder.append(String.format("CREATE TABLE %s (", TABLE_NAME));
+ TrackerEntry.buildCreationString(queryBuilder);
+
+ queryBuilder.append(");");
+ db.execSQL(queryBuilder.toString());
+ db.setVersion(DB_VERSION);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // TODO: reimplement this when dB version changes
+ Log.w(LOG_TAG, "Upgrading database from version " + oldVersion
+ + " to " + newVersion
+ + ", which will destroy all old data");
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
+ onCreate(db);
+ }
+ }
+
+ private DatabaseHelper mOpenHelper;
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ return true;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ int result = db.delete(TABLE_NAME, selection, selectionArgs);
+ getContext().getContentResolver().notifyChange(uri, null);
+ return result;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ long rowId = db.insert(TABLE_NAME, null, values);
+ if (rowId > 0) {
+ Uri addedUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
+ getContext().getContentResolver().notifyChange(addedUri, null);
+ return addedUri;
+ }
+ return null;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ // TODO: extract limit from URI ?
+ Cursor cursor = db.query(TABLE_NAME, projection, selection,
+ selectionArgs, null, null, sortOrder);
+ getContext().getContentResolver().notifyChange(uri, null);
+ return cursor;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}