~opensatnav-admins/opensatnav/release-1.0

« back to all changes in this revision

Viewing changes to src/org/opensatnav/android/contribute/services/TrackRecordingService.java

  • Committer: Kieran Fleming
  • Date: 2010-12-13 13:13:48 UTC
  • Revision ID: kieran.fleming@gmail.com-20101213131348-pixo12i0wjf11jk3
Add all the missing stuff from the failed package rename

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright 2008 Google Inc.
 
3
 * 
 
4
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 
5
 * use this file except in compliance with the License. You may obtain a copy of
 
6
 * the License at
 
7
 * 
 
8
 * http://www.apache.org/licenses/LICENSE-2.0
 
9
 * 
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
12
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
13
 * License for the specific language governing permissions and limitations under
 
14
 * the License.
 
15
 */
 
16
package org.opensatnav.android.contribute.services;
 
17
 
 
18
import java.util.Timer;
 
19
import java.util.TimerTask;
 
20
 
 
21
import org.opensatnav.android.R;
 
22
import org.opensatnav.android.OpenSatNavConstants;
 
23
import org.opensatnav.android.SatNavActivity;
 
24
import org.opensatnav.android.contribute.content.IProviderUtils;
 
25
import org.opensatnav.android.contribute.content.Track;
 
26
import org.opensatnav.android.contribute.content.TracksColumns;
 
27
import org.opensatnav.android.contribute.content.Waypoint;
 
28
import org.opensatnav.android.contribute.content.WaypointsColumns;
 
29
import org.opensatnav.android.contribute.services.AbsoluteLocationListenerPolicy;
 
30
import org.opensatnav.android.contribute.services.LocationListenerPolicy;
 
31
import org.opensatnav.android.contribute.util.MyTracksUtils;
 
32
import org.opensatnav.android.contribute.util.StringUtils;
 
33
import org.opensatnav.android.contribute.util.constants.OSMConstants;
 
34
import org.opensatnav.android.services.LocationHandler;
 
35
import org.opensatnav.android.stats.TripStatistics;
 
36
 
 
37
import org.opensatnav.android.contribute.services.ITrackRecordingService;
 
38
 
 
39
import android.app.Notification;
 
40
import android.app.NotificationManager;
 
41
import android.app.PendingIntent;
 
42
import android.app.Service;
 
43
import android.content.ContentValues;
 
44
import android.content.Context;
 
45
import android.content.Intent;
 
46
import android.content.SharedPreferences;
 
47
import android.database.Cursor;
 
48
import android.database.sqlite.SQLiteException;
 
49
import android.location.Location;
 
50
import android.location.LocationListener;
 
51
import android.location.LocationManager;
 
52
import android.net.Uri;
 
53
import android.os.Bundle;
 
54
import android.os.Handler;
 
55
import android.os.IBinder;
 
56
import android.os.PowerManager;
 
57
import android.os.PowerManager.WakeLock;
 
58
import android.util.Log;
 
59
 
 
60
/**
 
61
 * A background service that registers a location listener and records track
 
62
 * points. Track points are saved to the MyTracksProvider.
 
63
 * 
 
64
 * @author Leif Hendrik Wilden
 
65
 */
 
66
public class TrackRecordingService extends Service implements LocationListener {
 
67
 
 
68
        //TODO: unlink this
 
69
  private static final String STATISTICS_ICON_URL =
 
70
      "http://maps.google.com/mapfiles/ms/micons/ylw-pushpin.png";
 
71
 
 
72
  private NotificationManager notificationManager;
 
73
  private LocationManager locationManager;
 
74
  private WakeLock wakeLock;
 
75
 
 
76
  private int minRecordingInterval =
 
77
      OpenSatNavConstants.DEFAULT_MIN_RECORDING_INTERVAL;
 
78
  private int minRecordingDistance =
 
79
          OpenSatNavConstants.DEFAULT_MIN_RECORDING_DISTANCE;
 
80
  private int maxRecordingDistance =
 
81
          OpenSatNavConstants.DEFAULT_MAX_RECORDING_DISTANCE;
 
82
  private int minRequiredAccuracy =
 
83
          OpenSatNavConstants.DEFAULT_MIN_REQUIRED_ACCURACY;
 
84
  private long recordingTrackId = -1;
 
85
 
 
86
  private long currentWaypointId = -1;
 
87
 
 
88
  /**
 
89
   * For debugging. Keep track of calls to onCreate().
 
90
   */
 
91
  private boolean onCreateWasCalled = false;
 
92
 
 
93
  /** The timer posts a runnable to the main thread via this handler. */
 
94
  private final Handler handler = new Handler();
 
95
 
 
96
  /**
 
97
   * Utilities to deal with the database.
 
98
   */
 
99
  private IProviderUtils providerUtils;
 
100
 
 
101
  private TripStatistics stats = new TripStatistics();
 
102
  private TripStatistics waypointStats = new TripStatistics();
 
103
 
 
104
  /**
 
105
   * Current length of the recorded track. This length is calculated from the
 
106
   * recorded points (as compared to each location fix). It's used to overlay
 
107
   * waypoints precisely in the elevation profile chart.
 
108
   */
 
109
  private double length = 0;
 
110
 
 
111
  /**
 
112
   * Status announcer executer.
 
113
   */
 
114
  private PeriodicTaskExecuter executer;
 
115
  
 
116
  private SplitManager splitManager;
 
117
  
 
118
  /**
 
119
   * The interval in milliseconds that we have requested to be notfied of gps
 
120
   * readings.
 
121
   */
 
122
  private long currentRecordingInterval = 0;
 
123
 
 
124
  /**
 
125
   * The policy used to decide how often we should request gps updates.
 
126
   */
 
127
  private LocationListenerPolicy locationListenerPolicy =
 
128
      new AbsoluteLocationListenerPolicy(0);
 
129
 
 
130
  /**
 
131
   * The old location handler
 
132
   */
 
133
  protected static LocationHandler mLocationHandler;
 
134
 
 
135
  /**
 
136
   * Task invoked by a timer periodically to make sure the location listener is
 
137
   * still registered.
 
138
   */
 
139
  private TimerTask checkLocationListener = new TimerTask() {
 
140
    @Override
 
141
    public void run() {
 
142
      if (!onCreateWasCalled) {
 
143
        Log.e(OpenSatNavConstants.LOG_TAG,
 
144
            "TrackRecordingService is running, but onCreate not called.");
 
145
      }
 
146
      if (isRecording) {
 
147
        handler.post(new Runnable() {
 
148
          public void run() {
 
149
            Log.d(OpenSatNavConstants.LOG_TAG,
 
150
                "Re-registering location listener with TrackRecordingService.");
 
151
            unregisterLocationListener();
 
152
            registerLocationListener();
 
153
          }
 
154
        });
 
155
      } else {
 
156
        Log.w(OpenSatNavConstants.LOG_TAG,
 
157
            "Track recording service is paused. That should not be.");
 
158
      }
 
159
    }
 
160
  };
 
161
 
 
162
  /**
 
163
   * This timer invokes periodically the checkLocationListener timer task.
 
164
   */
 
165
  private final Timer timer = new Timer();
 
166
 
 
167
  /**
 
168
   * Is the phone currently moving?
 
169
   */
 
170
  private boolean isMoving = true;
 
171
 
 
172
  /**
 
173
   * Is the service currently recording a track?
 
174
   */
 
175
  private boolean isRecording = false;
 
176
 
 
177
  /**
 
178
   * Last good location the service has received from the location listener
 
179
   */
 
180
  private Location lastLocation = null;
 
181
 
 
182
  /**
 
183
   * Last valid location (i.e. not a marker) that was recorded.
 
184
   */
 
185
  private Location lastValidLocation = null;
 
186
 
 
187
  /*
 
188
   * Utility functions
 
189
   */
 
190
 
 
191
  /**
 
192
   * Inserts a new location in the track points db and updates the corresponding
 
193
   * track in the track db.
 
194
   * 
 
195
   * @param recordingTrack the track that is currently being recorded
 
196
   * @param location the location to be inserted
 
197
   * @param lastRecordedLocation the last recorded location before this one (or
 
198
   *        null if none)
 
199
   * @param lastRecordedLocationId the id of the last recorded location (or -1
 
200
   *        if none)
 
201
   * @param trackId the id of the track
 
202
   * @return true if successful. False if SQLite3 threw an exception.
 
203
   */
 
204
  private boolean insertLocation(Track recordingTrack, Location location,
 
205
      Location lastRecordedLocation, long lastRecordedLocationId,
 
206
      long trackId) {
 
207
 
 
208
    // Keep track of length along recorded track (needed when a waypoint is
 
209
    // inserted):
 
210
    if (MyTracksUtils.isValidLocation(location)) {
 
211
      if (lastValidLocation != null) {
 
212
        length += location.distanceTo(lastValidLocation);
 
213
      }
 
214
      lastValidLocation = location;
 
215
    }
 
216
 
 
217
    // Insert the new location:
 
218
    try {
 
219
      Uri pointUri = providerUtils.insertTrackPoint(location, trackId);
 
220
      int pointId = Integer.parseInt(pointUri.getLastPathSegment());
 
221
 
 
222
      // Update the current track:
 
223
      if (lastRecordedLocation != null
 
224
          && lastRecordedLocation.getLatitude() < 90) {
 
225
        ContentValues values = new ContentValues();
 
226
        if (recordingTrack.getStartId() < 0) {
 
227
          values.put(TracksColumns.STARTID, pointId);
 
228
          recordingTrack.setStartId(pointId);
 
229
        }
 
230
        values.put(TracksColumns.STOPID, pointId);
 
231
        values.put(TracksColumns.STOPTIME, System.currentTimeMillis());
 
232
        values.put(TracksColumns.NUMPOINTS,
 
233
            recordingTrack.getNumberOfPoints() + 1);
 
234
        values.put(TracksColumns.MINLAT, stats.getBottom());
 
235
        values.put(TracksColumns.MAXLAT, stats.getTop());
 
236
        values.put(TracksColumns.MINLON, stats.getLeft());
 
237
        values.put(TracksColumns.MAXLON, stats.getRight());
 
238
        values.put(TracksColumns.TOTALDISTANCE, stats.getTotalDistance());
 
239
        values.put(TracksColumns.TOTALTIME, stats.getTotalTime());
 
240
        values.put(TracksColumns.MOVINGTIME, stats.getMovingTime());
 
241
        values.put(TracksColumns.AVGSPEED, stats.getAverageSpeed());
 
242
        values.put(TracksColumns.AVGMOVINGSPEED, stats.getAverageMovingSpeed());
 
243
        values.put(TracksColumns.MAXSPEED, stats.getMaxSpeed());
 
244
        values.put(TracksColumns.MINELEVATION, stats.getMinElevation());
 
245
        values.put(TracksColumns.MAXELEVATION, stats.getMaxElevation());
 
246
        values.put(TracksColumns.ELEVATIONGAIN, stats.getTotalElevationGain());
 
247
        values.put(TracksColumns.MINGRADE, stats.getMinGrade());
 
248
        values.put(TracksColumns.MAXGRADE, stats.getMaxGrade());
 
249
        getContentResolver().update(TracksColumns.CONTENT_URI,
 
250
            values, "_id=" + recordingTrack.getId(), null);
 
251
        updateCurrentWaypoint();
 
252
      }
 
253
    } catch (SQLiteException e) {
 
254
      // Insert failed, most likely because of SqlLite error code 5
 
255
      // (SQLite_BUSY). This is expected to happen extremely rarely (if our
 
256
      // listener gets invoked twice at about the same time).
 
257
      Log.w(OpenSatNavConstants.LOG_TAG,
 
258
          "Caught SQLiteException: " + e.getMessage(), e);
 
259
      return false;
 
260
    }
 
261
    splitManager.updateSplits();
 
262
    return true;
 
263
  }
 
264
 
 
265
  private void updateCurrentWaypoint() {
 
266
    if (currentWaypointId >= 0) {
 
267
      ContentValues values = new ContentValues();
 
268
      values.put(WaypointsColumns.STARTTIME, waypointStats.getStartTime());
 
269
      values.put(WaypointsColumns.LENGTH, length);
 
270
      values.put(WaypointsColumns.DURATION,
 
271
          System.currentTimeMillis() - stats.getStartTime());
 
272
      values.put(WaypointsColumns.TOTALDISTANCE,
 
273
          waypointStats.getTotalDistance());
 
274
      values.put(WaypointsColumns.TOTALTIME, waypointStats.getTotalTime());
 
275
      values.put(WaypointsColumns.MOVINGTIME, waypointStats.getMovingTime());
 
276
      values.put(WaypointsColumns.AVGSPEED, waypointStats.getAverageSpeed());
 
277
      values.put(WaypointsColumns.AVGMOVINGSPEED,
 
278
          waypointStats.getAverageMovingSpeed());
 
279
      values.put(WaypointsColumns.MAXSPEED, waypointStats.getMaxSpeed());
 
280
      values.put(WaypointsColumns.MINELEVATION,
 
281
          waypointStats.getMinElevation());
 
282
      values.put(WaypointsColumns.MAXELEVATION,
 
283
          waypointStats.getMaxElevation());
 
284
      values.put(WaypointsColumns.ELEVATIONGAIN,
 
285
          waypointStats.getTotalElevationGain());
 
286
      values.put(WaypointsColumns.MINGRADE, waypointStats.getMinGrade());
 
287
      values.put(WaypointsColumns.MAXGRADE, waypointStats.getMaxGrade());
 
288
      getContentResolver().update(WaypointsColumns.CONTENT_URI,
 
289
          values, "_id=" + currentWaypointId, null);
 
290
    }
 
291
  }
 
292
 
 
293
  /**
 
294
   * Tries to acquire a partial wake lock if not already acquired. Logs errors
 
295
   * and gives up trying in case the wake lock cannot be acquired.
 
296
   */
 
297
  public void acquireWakeLock() {
 
298
    try {
 
299
      PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 
300
      if (pm == null) {
 
301
        Log.e(OpenSatNavConstants.LOG_TAG,
 
302
            "TrackRecordingService: Power manager not found!");
 
303
        return;
 
304
      }
 
305
      if (wakeLock == null) {
 
306
        wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
 
307
            OpenSatNavConstants.LOG_TAG);
 
308
        if (wakeLock == null) {
 
309
          Log.e(OpenSatNavConstants.LOG_TAG,
 
310
              "TrackRecordingService: Could not create wake lock (null).");
 
311
          return;
 
312
        }
 
313
      }
 
314
      if (!wakeLock.isHeld()) {
 
315
        wakeLock.acquire();
 
316
        if (!wakeLock.isHeld()) {
 
317
          Log.e(OpenSatNavConstants.LOG_TAG,
 
318
              "TrackRecordingService: Could not acquire wake lock.");
 
319
        }
 
320
      }
 
321
    } catch (RuntimeException e) {
 
322
      Log.e(OpenSatNavConstants.LOG_TAG,
 
323
          "TrackRecordingService: Caught unexpected exception: "
 
324
          + e.getMessage(), e);
 
325
    }
 
326
  }
 
327
 
 
328
  /**
 
329
   * Shows the notification message and icon in the notification bar.
 
330
   */
 
331
  public void showNotification() {
 
332
    if (isRecording) {
 
333
        Notification notification = new Notification(R.drawable.icon, getString(R.string.start_trace_ticker), System.currentTimeMillis());
 
334
      PendingIntent contentIntent = PendingIntent.getActivity(
 
335
          this, 0 /* requestCode */, new Intent(this, SatNavActivity.class),
 
336
          0 /* flags */);
 
337
      notification.setLatestEventInfo(this, getString(R.string.app_name),
 
338
          getString(R.string.tracing_notification_text), contentIntent);
 
339
      notification.flags += Notification.FLAG_NO_CLEAR;
 
340
      notificationManager.notify(1, notification);
 
341
    } else {
 
342
      notificationManager.cancelAll();
 
343
    }
 
344
  }
 
345
 
 
346
  public void registerLocationListener() {
 
347
    if (locationManager == null) {
 
348
      Log.e(OpenSatNavConstants.LOG_TAG,
 
349
          "TrackRecordingService: Do not have any location manager.");
 
350
      return;
 
351
    }
 
352
    Log.d(OpenSatNavConstants.LOG_TAG,
 
353
        "Preparing to register location listener w/ TrackRecordingService...");
 
354
    try {
 
355
      long desiredInterval = locationListenerPolicy.getDesiredPollingInterval();
 
356
      locationManager.requestLocationUpdates(
 
357
          OpenSatNavConstants.GPS_PROVIDER, desiredInterval,
 
358
          locationListenerPolicy.getMinDistance(),
 
359
          // , 0 /* minDistance, get all updates to properly time pauses */
 
360
          TrackRecordingService.this);
 
361
      currentRecordingInterval = desiredInterval;
 
362
      Log.d(OpenSatNavConstants.LOG_TAG,
 
363
          "...location listener now registered w/ TrackRecordingService @ "
 
364
          + currentRecordingInterval);
 
365
    } catch (RuntimeException e) {
 
366
      Log.e(OpenSatNavConstants.LOG_TAG,
 
367
          "Could not register location listener: " + e.getMessage(), e);
 
368
    }
 
369
  }
 
370
 
 
371
  public void unregisterLocationListener() {
 
372
    if (locationManager == null) {
 
373
      Log.e(OpenSatNavConstants.LOG_TAG,
 
374
          "TrackRecordingService: Do not have any location manager.");
 
375
      return;
 
376
    }
 
377
    locationManager.removeUpdates(this);
 
378
    Log.d(OpenSatNavConstants.LOG_TAG,
 
379
        "Location listener now unregistered w/ TrackRecordingService.");
 
380
  }
 
381
 
 
382
  private void restoreStats() {
 
383
    if (recordingTrackId < 0) {
 
384
      return;
 
385
    }
 
386
 
 
387
    Track track = providerUtils.getTrack(recordingTrackId);
 
388
    if (track == null) {
 
389
      return;
 
390
    }
 
391
 
 
392
    stats = new TripStatistics(track.getStartTime());
 
393
    
 
394
    
 
395
    splitManager.restore();
 
396
    length = 0;
 
397
    lastValidLocation = null;
 
398
 
 
399
    Waypoint waypoint = providerUtils.getFirstWaypoint(recordingTrackId);
 
400
    if (waypoint != null) {
 
401
      currentWaypointId = waypoint.getId();
 
402
      waypointStats = new TripStatistics(waypoint);
 
403
    } else {
 
404
      // This should never happen, but we got to do something so life goes on:
 
405
      waypointStats = new TripStatistics(track.getStartTime());
 
406
      currentWaypointId = -1;
 
407
    }
 
408
 
 
409
    Cursor cursor = null;
 
410
    try {
 
411
      cursor = providerUtils.getLocationsCursor(
 
412
          recordingTrackId, -1, OSMConstants.MAX_LOADED_TRACK_POINTS,
 
413
          true);
 
414
      if (cursor != null) {
 
415
        if (cursor.moveToLast()) {
 
416
          do {
 
417
            Location location = providerUtils.createLocation(cursor);
 
418
            if (MyTracksUtils.isValidLocation(location)) {
 
419
              stats.addLocation(location, location.getTime());
 
420
              if (lastValidLocation != null) {
 
421
                length += location.distanceTo(lastValidLocation);
 
422
              }
 
423
              lastValidLocation = location;
 
424
            }
 
425
          } while (cursor.moveToPrevious());
 
426
        }
 
427
        stats.setMovingTime(track.getMovingTime());
 
428
        stats.pauseAt(track.getStopTime());
 
429
        stats.resume();
 
430
      } else {
 
431
        Log.e(OpenSatNavConstants.LOG_TAG, "Could not get track points cursor.");
 
432
      }
 
433
    } catch (RuntimeException e) {
 
434
      Log.e(OpenSatNavConstants.LOG_TAG, "Error while restoring track.", e);
 
435
    } finally {
 
436
      if (cursor != null) {
 
437
        cursor.close();
 
438
      }
 
439
    }
 
440
 
 
441
    splitManager.calculateNextSplit();
 
442
  }
 
443
 
 
444
  /*
 
445
   * Location listener implementation: =================================
 
446
   */
 
447
 
 
448
  @Override
 
449
  public void onLocationChanged(Location location) {
 
450
    
 
451
 
 
452
    try {
 
453
      // Don't record if the service has been asked to pause recording:
 
454
      if (!isRecording) {
 
455
        Log.w(OpenSatNavConstants.LOG_TAG,
 
456
            "Not recording because recording has been paused.");
 
457
        return;
 
458
      }
 
459
 
 
460
      // This should never happen, but just in case (we really don't want the
 
461
      // service
 
462
      // to crash):
 
463
      if (location == null) {
 
464
        Log.w(OpenSatNavConstants.LOG_TAG,
 
465
            "Location changed, but location is null.");
 
466
        return;
 
467
      }
 
468
 
 
469
      // Don't record if the accuracy is too bad:
 
470
      if (location.getAccuracy() > minRequiredAccuracy) {
 
471
        Log.d(OpenSatNavConstants.LOG_TAG,
 
472
            "Not recording. Bad accuracy.");
 
473
        return;
 
474
      }
 
475
 
 
476
      // At least one track must be available for appending points:
 
477
      Track recordingTrack = providerUtils.getTrack(recordingTrackId);
 
478
      if (recordingTrack == null) {
 
479
        Log.d(OpenSatNavConstants.LOG_TAG,
 
480
            "Not recording. No track to append to available.");
 
481
        return;
 
482
      }
 
483
 
 
484
      if (MyTracksUtils.isValidLocation(location)) {
 
485
        long now = System.currentTimeMillis();
 
486
        stats.addLocation(location, now);
 
487
        waypointStats.addLocation(location, now);
 
488
      }
 
489
 
 
490
      // Update the idle time if needed.
 
491
      locationListenerPolicy.updateIdleTime(stats.getIdleTime());
 
492
      if (currentRecordingInterval
 
493
          != locationListenerPolicy.getDesiredPollingInterval()) {
 
494
        registerLocationListener();
 
495
      }
 
496
 
 
497
      Location lastRecordedLocation = providerUtils.getLastLocation();
 
498
      long lastRecordedLocationId =
 
499
          providerUtils.getLastLocationId(recordingTrackId);
 
500
      double distanceToLastRecorded = Double.POSITIVE_INFINITY;
 
501
      if (lastRecordedLocation != null) {
 
502
        distanceToLastRecorded = location.distanceTo(lastRecordedLocation);
 
503
      }
 
504
      double distanceToLast = Double.POSITIVE_INFINITY;
 
505
      if (lastLocation != null) {
 
506
        distanceToLast = location.distanceTo(lastLocation);
 
507
      }
 
508
 
 
509
      // If the user has been stationary for two recording just record the first
 
510
      // two and ignore the rest. This code will only have an effect if the
 
511
      // maxRecordingDistance = 0
 
512
      if (distanceToLast == 0) {
 
513
        if (isMoving) {
 
514
          Log.d(OpenSatNavConstants.LOG_TAG, "Found two identical locations.");
 
515
          isMoving = false;
 
516
          if (lastLocation != null && lastRecordedLocation != null
 
517
              && !lastRecordedLocation.equals(lastLocation)) {
 
518
            // Need to write the last location. This will happen when
 
519
            // lastRecordedLocation.distance(lastLocation) <
 
520
            // minRecordingDistance
 
521
            if (!insertLocation(recordingTrack, lastLocation,
 
522
                lastRecordedLocation, lastRecordedLocationId,
 
523
                recordingTrackId)) {
 
524
              return;
 
525
            }
 
526
            lastRecordedLocationId++;
 
527
          }
 
528
        } else {
 
529
          Log.d(OpenSatNavConstants.LOG_TAG,
 
530
              "Not recording. More than two identical locations.");
 
531
        }
 
532
      } else if (distanceToLastRecorded > minRecordingDistance) {
 
533
        if (lastLocation != null && !isMoving) {
 
534
          // Last location was the last stationary location. Need to go back and
 
535
          // add it.
 
536
          if (!insertLocation(recordingTrack, lastLocation,
 
537
              lastRecordedLocation, lastRecordedLocationId, recordingTrackId)) {
 
538
            return;
 
539
          }
 
540
          lastRecordedLocationId++;
 
541
          isMoving = true;
 
542
        }
 
543
 
 
544
        // If separation from last recorded point is too large insert a
 
545
        // separator
 
546
        // to indicate end of a segment:
 
547
        boolean startNewSegment =
 
548
            lastRecordedLocation != null
 
549
                && lastRecordedLocation.getLatitude() < 90
 
550
                && distanceToLastRecorded > maxRecordingDistance
 
551
                && recordingTrack.getStartId() >= 0;
 
552
        if (startNewSegment) {
 
553
          // Insert a separator point to indicate start of new track:
 
554
          Log.d(OpenSatNavConstants.LOG_TAG, "Inserting a separator.");
 
555
          Location separator = new Location(OpenSatNavConstants.GPS_PROVIDER);
 
556
          separator.setLongitude(0);
 
557
          separator.setLatitude(100);
 
558
          //TODO: This seems like a bit of a hack, see if this can be nicer
 
559
          separator.setTime(lastRecordedLocation.getTime());
 
560
          providerUtils.insertTrackPoint(separator, recordingTrackId);
 
561
        }
 
562
 
 
563
        if (!insertLocation(recordingTrack, location, lastRecordedLocation,
 
564
            lastRecordedLocationId, recordingTrackId)) {
 
565
          return;
 
566
        }
 
567
      } else {
 
568
        Log.d(OpenSatNavConstants.LOG_TAG, String.format(
 
569
            "Not recording. Distance to last recorded point (%f m) is less than"
 
570
            + " %d m.", distanceToLastRecorded, minRecordingDistance));
 
571
      }
 
572
    } catch (Error e) {
 
573
      // Probably important enough to rethrow.
 
574
      Log.e(OpenSatNavConstants.LOG_TAG, "Error in onLocationChanged", e);
 
575
      throw e;
 
576
    } catch (RuntimeException e) {
 
577
      // Safe usually to trap exceptions.
 
578
      Log.e(OpenSatNavConstants.LOG_TAG,
 
579
          "Trapping exception in onLocationChanged", e);
 
580
      throw e;
 
581
    }
 
582
    lastLocation = location;
 
583
  }
 
584
 
 
585
  @Override
 
586
  public void onProviderDisabled(String provider) {
 
587
    // Do nothing
 
588
  }
 
589
 
 
590
  @Override
 
591
  public void onProviderEnabled(String provider) {
 
592
    // Do nothing
 
593
  }
 
594
 
 
595
  @Override
 
596
  public void onStatusChanged(String provider, int status, Bundle extras) {
 
597
    // Do nothing
 
598
  }
 
599
 
 
600
  /*
 
601
   * SharedPreferencesChangeListener interface implementation. Note that
 
602
   * services don't currently receive this event (Android platform limitation).
 
603
   * This should be called from an activity whenever settings change.
 
604
   */
 
605
 
 
606
  /**
 
607
   * Notifies that preferences have changed. 
 
608
   * Call this with key == null to update all preferences in one call.
 
609
   *
 
610
   * @param key the key that changed (may be null to update all preferences)
 
611
   */
 
612
  public void onSharedPreferenceChanged(String key) {
 
613
    Log.d(OpenSatNavConstants.LOG_TAG,
 
614
        "TrackRecordingService.onSharedPreferenceChanged");
 
615
    SharedPreferences sharedPreferences =
 
616
        getSharedPreferences(OpenSatNavConstants.SETTINGS_NAME, 0);
 
617
    if (sharedPreferences == null) {
 
618
      Log.w(OpenSatNavConstants.LOG_TAG,
 
619
          "TrackRecordingService: Couldn't get shared preferences.");
 
620
      return;
 
621
    }
 
622
 
 
623
    if (key == null || key.equals(OpenSatNavConstants.MIN_RECORDING_DISTANCE)) {
 
624
      minRecordingDistance = sharedPreferences.getInt(
 
625
          OpenSatNavConstants.MIN_RECORDING_DISTANCE,
 
626
          OpenSatNavConstants.DEFAULT_MIN_RECORDING_DISTANCE);
 
627
      Log.d(OpenSatNavConstants.LOG_TAG,
 
628
          "TrackRecordingService: minRecordingDistance = "
 
629
          + minRecordingDistance);
 
630
    }
 
631
    /*if (key == null || key.equals(OpenSatNavConstants.MAX_RECORDING_DISTANCE)) {
 
632
      maxRecordingDistance = sharedPreferences.getInt(
 
633
          OpenSatNavConstants.MAX_RECORDING_DISTANCE,
 
634
          OpenSatNavConstants.DEFAULT_MAX_RECORDING_DISTANCE);
 
635
    }*/
 
636
    maxRecordingDistance = OpenSatNavConstants.DEFAULT_MAX_RECORDING_DISTANCE;
 
637
    /**Fix this, and put it in settins */
 
638
    
 
639
    
 
640
    if (key == null || key.equals(OpenSatNavConstants.MIN_RECORDING_INTERVAL)) {
 
641
      minRecordingInterval = sharedPreferences.getInt(
 
642
          OpenSatNavConstants.MIN_RECORDING_INTERVAL,
 
643
          OpenSatNavConstants.DEFAULT_MIN_RECORDING_INTERVAL);
 
644
      switch (minRecordingInterval) {
 
645
        case -2:
 
646
          // Battery Miser
 
647
          // min: 30 seconds
 
648
          // max: 5 minutes
 
649
          // minDist: 5 meters Choose battery life over moving time accuracy.
 
650
          locationListenerPolicy =
 
651
              new AdaptiveLocationListenerPolicy(30000, 300000, 5);
 
652
          break;
 
653
        case -1:
 
654
          // High Accuracy
 
655
          // min: 1 second
 
656
          // max: 30 seconds
 
657
          // minDist: 0 meters get all updates to properly measure moving time.
 
658
          locationListenerPolicy =
 
659
              new AdaptiveLocationListenerPolicy(1000, 30000, 0);
 
660
          break;
 
661
        default:
 
662
          locationListenerPolicy =
 
663
              new AbsoluteLocationListenerPolicy(minRecordingInterval * 1000);
 
664
      }
 
665
    }
 
666
    if (key == null || key.equals(OpenSatNavConstants.MIN_REQUIRED_ACCURACY)) {
 
667
      minRequiredAccuracy = sharedPreferences.getInt(
 
668
          OpenSatNavConstants.MIN_REQUIRED_ACCURACY,
 
669
          OpenSatNavConstants.DEFAULT_MIN_REQUIRED_ACCURACY);
 
670
    }
 
671
    if (key == null || key.equals(OpenSatNavConstants.RECORDING_TRACK)) {
 
672
      recordingTrackId =
 
673
          sharedPreferences.getLong(OpenSatNavConstants.RECORDING_TRACK, -1);
 
674
    }
 
675
    
 
676
    /*if (key == null || key.equals(OpenSatNavConstants.SPLIT_FREQUENCY)) {
 
677
      splitManager.setSplitFrequency(
 
678
          sharedPreferences.getInt(OpenSatNavConstants.SPLIT_FREQUENCY, 0));
 
679
    }
 
680
    if (key == null || key.equals(OpenSatNavConstants.SIGNAL_SAMPLING_FREQUENCY)) {
 
681
      signalManager.setFrequency(sharedPreferences.getInt(
 
682
          OpenSatNavConstants.SIGNAL_SAMPLING_FREQUENCY, -1), this);
 
683
    }
 
684
    if (key == null || key.equals(OpenSatNavConstants.METRIC_UNITS)) {
 
685
      splitManager.setMetricUnits(
 
686
          sharedPreferences.getBoolean(OpenSatNavConstants.METRIC_UNITS, true));
 
687
    }*/
 
688
    /**TODO: Put these in settings too: */
 
689
    splitManager.setSplitFrequency(0);
 
690
    
 
691
    splitManager.setMetricUnits(true);
 
692
    
 
693
 
 
694
    if (isRecording) {
 
695
      registerLocationListener();
 
696
    }
 
697
  }
 
698
 
 
699
  /*
 
700
   * Application lifetime events: ============================
 
701
   */
 
702
 
 
703
  @Override
 
704
  public void onCreate() {
 
705
    Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.onCreate");
 
706
    super.onCreate();
 
707
    onCreateWasCalled = true;
 
708
    setForeground(true);
 
709
    
 
710
    providerUtils = IProviderUtils.Factory.get(this);
 
711
    notificationManager =
 
712
        (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 
713
    locationManager =
 
714
        (LocationManager) getSystemService(Context.LOCATION_SERVICE);
 
715
    splitManager = new SplitManager(this);
 
716
    
 
717
    onSharedPreferenceChanged(null);
 
718
    restoreStats();
 
719
    registerLocationListener();
 
720
    Log.v("TTT", "Before");
 
721
    acquireWakeLock();
 
722
    Log.v("TTT", "After");
 
723
    /**
 
724
     * After 5 min, check every minute that location listener still is
 
725
     * registered and spit out additional debugging info to the logs:
 
726
     */
 
727
    timer.schedule(checkLocationListener, 1000 * 60 * 5, 1000 * 60);
 
728
    isRecording = true;
 
729
    
 
730
  }
 
731
 
 
732
  @Override
 
733
  public void onDestroy() {
 
734
    Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.onDestroy");
 
735
    if (wakeLock != null && wakeLock.isHeld()) {
 
736
      wakeLock.release();
 
737
    }
 
738
    isRecording = false;
 
739
    showNotification();
 
740
    unregisterLocationListener();
 
741
    if (executer != null) {
 
742
      executer.shutdown();
 
743
    }
 
744
    splitManager.shutdown();
 
745
    super.onDestroy();
 
746
  }
 
747
 
 
748
  @Override
 
749
  public IBinder onBind(Intent intent) {
 
750
    Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.onBind");
 
751
    return binder;
 
752
  }
 
753
 
 
754
  @Override
 
755
  public boolean onUnbind(Intent intent) {
 
756
    Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.onUnbind");
 
757
    return super.onUnbind(intent);
 
758
  }
 
759
 
 
760
  @Override
 
761
  public boolean stopService(Intent name) {
 
762
    Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.stopService");
 
763
    unregisterLocationListener();
 
764
    return super.stopService(name);
 
765
  }
 
766
 
 
767
  public long insertWaypointMarker(Waypoint waypoint) {
 
768
    if (waypoint.getLocation() != null) {
 
769
      waypoint.setLength(length);
 
770
      waypoint.setDuration(
 
771
          waypoint.getLocation().getTime() - stats.getStartTime());
 
772
      Uri uri = providerUtils.insertWaypoint(waypoint);
 
773
      return Long.parseLong(uri.getLastPathSegment());
 
774
    }
 
775
    return -1;
 
776
  }
 
777
 
 
778
  /**
 
779
   * Inserts a statistics marker. A statistics marker holds the stats for the
 
780
   * last segment up to this marker.
 
781
   * 
 
782
   * @param location the location where to insert
 
783
   * @return the unique id of the inserted marker
 
784
   */
 
785
  public long insertStatisticsMarker(Location location) {
 
786
    Waypoint waypoint = new Waypoint();
 
787
    waypoint.setTrackId(recordingTrackId);
 
788
    waypoint.setType(Waypoint.TYPE_STATISTICS);
 
789
    waypoint.setName(TrackRecordingService.this.getString(R.string.statistics));
 
790
    waypointStats.fillStatisticsForWaypoint(waypoint);
 
791
    StringUtils utils = new StringUtils(TrackRecordingService.this);
 
792
    waypoint.setDescription(utils.generateWaypointDescription(waypoint));
 
793
    waypoint.setLocation(location);
 
794
    waypoint.setIcon(STATISTICS_ICON_URL);
 
795
    waypoint.setLength(length);
 
796
    final long time = System.currentTimeMillis();
 
797
    waypoint.setDuration(time - stats.getStartTime());
 
798
    waypoint.setStartTime(waypointStats.getStartTime());
 
799
    waypoint.setStartId(providerUtils.getLastLocationId(recordingTrackId));
 
800
    Uri uri = providerUtils.insertWaypoint(waypoint);
 
801
    waypointStats = new TripStatistics(time);
 
802
    updateCurrentWaypoint();
 
803
    return Long.parseLong(uri.getLastPathSegment());
 
804
  }
 
805
 
 
806
  /**
 
807
   * The ITrackRecordingService is defined through IDL.
 
808
   */
 
809
  private final ITrackRecordingService.Stub binder =
 
810
      new ITrackRecordingService.Stub() {
 
811
        @Override
 
812
        public boolean isRecording() {
 
813
          return isRecording;
 
814
        }
 
815
 
 
816
        @Override
 
817
        public long getRecordingTrackId() {
 
818
          return recordingTrackId;
 
819
        }
 
820
 
 
821
        @Override
 
822
        public boolean hasRecorded() {
 
823
          return providerUtils.getLastTrackId() >= 0;
 
824
        }
 
825
    
 
826
        @Override
 
827
        public long startNewTrack() {
 
828
          Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.startNewTrack");
 
829
          Track track = new Track();
 
830
          track.setName("new");
 
831
          track.setStartTime(System.currentTimeMillis());
 
832
          track.setStartId(-1);
 
833
          Uri trackUri = providerUtils.insertTrack(track);
 
834
          long trackId = Long.parseLong(trackUri.getLastPathSegment());
 
835
          track.setId(trackId);
 
836
          track.setName(String.format(getString(R.string.new_track), trackId));
 
837
          providerUtils.updateTrack(track);
 
838
          recordingTrackId = trackId;
 
839
          currentWaypointId = insertStatisticsMarker(null);
 
840
          isRecording = true;
 
841
          isMoving = true;
 
842
          stats = new TripStatistics(track.getStartTime());
 
843
          length = 0;
 
844
          showNotification();
 
845
          registerLocationListener();
 
846
          splitManager.restore();
 
847
          
 
848
          return trackId;
 
849
        }
 
850
    
 
851
        /**
 
852
         * Insert the given waypoint marker. Users can insert waypoint markers
 
853
         * to tag locations with a name, description, category etc.
 
854
         * 
 
855
         * @param waypoint a waypoint
 
856
         * @return the unique id of the inserted marker
 
857
         */
 
858
        @Override
 
859
        public long insertWaypointMarker(Waypoint waypoint) {
 
860
          return TrackRecordingService.this.insertWaypointMarker(waypoint);
 
861
        }
 
862
    
 
863
        /**
 
864
         * Insert a statistics marker. A statistics marker holds the stats for
 
865
         * the last segment up to this marker.
 
866
         * 
 
867
         * @param location the location where to insert
 
868
         * @return the unique id of the inserted marker
 
869
         */
 
870
        @Override
 
871
        public long insertStatisticsMarker(Location location) {
 
872
          return TrackRecordingService.this.insertStatisticsMarker(location);
 
873
        }
 
874
    
 
875
        @Override
 
876
        public void endCurrentTrack() {
 
877
          Log.d(OpenSatNavConstants.LOG_TAG, "TrackRecordingService.endCurrentTrack");
 
878
          isRecording = false;
 
879
          Track recordingTrack = providerUtils.getTrack(recordingTrackId);
 
880
          if (recordingTrack != null) {
 
881
            recordingTrack.setStopTime(System.currentTimeMillis());
 
882
            recordingTrack.setTotalTime(
 
883
                recordingTrack.getStopTime() - recordingTrack.getStartTime());
 
884
            long lastRecordedLocationId =
 
885
                providerUtils.getLastLocationId(recordingTrackId);
 
886
            ContentValues values = new ContentValues();
 
887
            if (lastRecordedLocationId >= 0
 
888
                && recordingTrack.getStopId() >= 0) {
 
889
              values.put(TracksColumns.STOPID, lastRecordedLocationId);
 
890
            }
 
891
            values.put(TracksColumns.STOPTIME, recordingTrack.getStopTime());
 
892
            values.put(TracksColumns.TOTALTIME, recordingTrack.getTotalTime());
 
893
            getContentResolver().update(TracksColumns.CONTENT_URI, values,
 
894
                "_id=" + recordingTrack.getId(), null);
 
895
          }
 
896
          showNotification();
 
897
          recordingTrackId = -1;
 
898
        }
 
899
    
 
900
        @Override
 
901
        public void deleteAllTracks() {
 
902
          endCurrentTrack();
 
903
          providerUtils.deleteAllTracks();
 
904
        }
 
905
    
 
906
        @Override
 
907
        public void recordLocation(Location loc) {
 
908
          onLocationChanged(loc);
 
909
        }
 
910
    
 
911
        @Override
 
912
        public void sharedPreferenceChanged(String key) {
 
913
          Log.d(OpenSatNavConstants.LOG_TAG,
 
914
              "TrackRecordingService.sharedPrefereneChange");
 
915
          onSharedPreferenceChanged(key);
 
916
        }
 
917
      };
 
918
 
 
919
  TripStatistics getTripStatistics() {
 
920
    return stats;
 
921
  }
 
922
 
 
923
  Location getLastLocation() {
 
924
    return lastLocation;
 
925
  }
 
926
 
 
927
  long getRecordingTrackId() {
 
928
    return recordingTrackId;
 
929
  }
 
930
}