This
section discusses the design considerations for service implementation.
There are two tracking mode. When the user clicks the start button in start tab, the onStartBtnClick handler passes the parameters (i.e., input type (GPS/automatic) and activity type) to MapDisplayActivity. MapDisplayActivity is also used to display the history entry, so it needs to determine if it should display a history entry or start a new entry. If it needs to display an entry, then HostoryTabFragment should pass the row id to it so that it can retrieve the entry from the database.
If MapDisplayActivity needs to record a new workout, it starts the tracking service, get the exercise entry from the service, then update the map when it receives an update from the tracking service. The following flow chart shows the work flow of tracking service.
You need to start the service explicitly instead of using the bind the service only (you can try to figure out why yourself). Also, you need to make sure the activity will not leak bound services. You can check this by inspecting logcat output. The key idea is that TrackingService creates an exercise entry and updates it when a new location update or activity inference update is available. The TrackingService sends a message to the MapDisplayActivity, so that the activity can update the map in a timely fashion. MapDisplayActivity gets the exercise entry when it is bound to the service, and saves the entry in the database once the tracking is completed.
Hints:
In the automatic mode, the app continually infers the activity type using accelerometer data. The activity classifier is built using Weka. The following figure shows the activity classification process. TrackingService should implement onSensorChanged() interface to receive accelerometer updates. Once it received an update, it put the accelerometer reading into a data queue. A working thread continuously gets readings from the queue.
Once the app has 64 readings, a feature vector is generated which will be the input of Weka classifier. The classifier generates a label that indicates user’s current activity (e.g., walking). The label is then updated in the ExcerciseEntry. A basic counting approach determines the overall activity based on the ratios of each label; that is if walking and running are measured the activity with the most labels become the overall label for the period.
The feature vector is generated as follows:
max = max(accBlock);
// Compute the re and im:
// setting values of re and im by reference.
fft.fft(re, im);
for (int i = 0; i < re.length; i++) {
// Compute each coefficient
double mag = Math.sqrt(re[i] * re[i] + im[i]* im[i]);
// Adding the computed FFT coefficient to the
// featVect
featVect.add(Double.valueOf(mag));
// Clear the field
im[i] = .0;
}
// Finally, append max after frequency components
featVect.add(Double.valueOf(max));
To
collect the training data, import the myrunsdatacollector.zip
to Android Studio, build it and install it on your phone. This is the data
collector app that you will use to collect training data to create
classification model in Weka. The collector allows you to label different
activities. The output of the collector is a single file called feature.arff, which is input to Weka to create the
classifier.
When the user clicks “Sync”, all local history entries will be uploaded to Google App Engine. Users can view the entry from browser. When the user clicks one entry’s “DELETE” button, the entry should be deleted both remotely on the cloud and locally on the phone. Make sure that you are deploying the server to the cloud. An example web interface is shown below.
The following figure shows how the system works. The app registers Google Cloud Messaging (GCM) in the MainActivity to receive entry deletion updates from the server. When the app is registered with GCM, it sends its device id to the server. The GcmRegistrationAsyncTask in the MainActivity, sends the device id to the server. In the server, it is handled by the RegistrationEndPoint, which then saves the device id into the datastore. When the user clicks on the ’Sync’ button, the StartTabFragment retrieves all the entries from the database, and then converts them into JSON format. ServerUtilities then posts the JSON format data to server’s PostDataServlet which in turn save the data to the datastore. In the server, the MyRunsAppEngineServlet fetches all the entries from the datastore and displays them on the web interface as a HTML table. When the user clicks the delete button, it passes through SendDeleteMessageServlet, which deletes the particular entry from the datastore and redirects to MyRunsAppEngineServlet. In the meantime the SendDeleteMessageServlet also sends a GCM message using MessagingEndPoint to the device, which contains the ID of the field to be deleted. This message is received by the GCMBroadcastReceiver, and the GCMIntentReceiver is invoked, which in turn deletes the entry from the local database.
MyRuns_Lab6_AppEngineServlet provides user the list of all entries (as shown in the first picture). When user clicks the “Delete” button, the request will be posted to SendDeleteMessageServlet where a message will be sent to the app and the selected entry will be deleted from the datastore. When the deletion is done, it should redirect to MyRunsAppEngineServlet to show the entry list. The entry should has been remove.
Hint: