diff --git a/Chapter 02 - Services/SimpleService/.idea/.name b/Chapter 02 - Services/SimpleService/.idea/.name
new file mode 100644
index 0000000..fbfcdf8
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/.name
@@ -0,0 +1 @@
+SimpleService
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/checkstyle-idea.xml b/Chapter 02 - Services/SimpleService/.idea/checkstyle-idea.xml
new file mode 100644
index 0000000..35ee4f5
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/checkstyle-idea.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/compiler.xml b/Chapter 02 - Services/SimpleService/.idea/compiler.xml
new file mode 100644
index 0000000..217af47
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/compiler.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/copyright/profiles_settings.xml b/Chapter 02 - Services/SimpleService/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/dictionaries/Igor.xml b/Chapter 02 - Services/SimpleService/.idea/dictionaries/Igor.xml
new file mode 100644
index 0000000..e1694e4
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/dictionaries/Igor.xml
@@ -0,0 +1,8 @@
+
+
+
+ advancedandroidbook
+ simpleservice
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/encodings.xml b/Chapter 02 - Services/SimpleService/.idea/encodings.xml
new file mode 100644
index 0000000..e206d70
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/encodings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/findbugs-idea.xml b/Chapter 02 - Services/SimpleService/.idea/findbugs-idea.xml
new file mode 100644
index 0000000..3d02d13
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/findbugs-idea.xml
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/gradle.xml b/Chapter 02 - Services/SimpleService/.idea/gradle.xml
new file mode 100644
index 0000000..736c7b5
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/libraries/support_annotations_21_0_3.xml b/Chapter 02 - Services/SimpleService/.idea/libraries/support_annotations_21_0_3.xml
new file mode 100644
index 0000000..7c7eed4
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/libraries/support_annotations_21_0_3.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/libraries/support_v4_21_0_3.xml b/Chapter 02 - Services/SimpleService/.idea/libraries/support_v4_21_0_3.xml
new file mode 100644
index 0000000..4f716bf
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/libraries/support_v4_21_0_3.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/misc.xml b/Chapter 02 - Services/SimpleService/.idea/misc.xml
new file mode 100644
index 0000000..df81b8d
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/misc.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Android API 21 Platform
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/modules.xml b/Chapter 02 - Services/SimpleService/.idea/modules.xml
new file mode 100644
index 0000000..5859a2f
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/scopes/scope_settings.xml b/Chapter 02 - Services/SimpleService/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..922003b
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/.idea/vcs.xml b/Chapter 02 - Services/SimpleService/.idea/vcs.xml
new file mode 100644
index 0000000..def6a6a
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/.idea/workspace.xml b/Chapter 02 - Services/SimpleService/.idea/workspace.xml
new file mode 100644
index 0000000..65d2d23
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/.idea/workspace.xml
@@ -0,0 +1,1754 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+ 5050
+
+
+
+
+
+
+
+
+
+ 1425301741483
+ 1425301741483
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ file://$PROJECT_DIR$/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
+ 115
+
+
+
+ file://$PROJECT_DIR$/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
+ 99
+
+
+
+
+ file://$PROJECT_DIR$/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
+ 75
+
+
+
+
+ file://$PROJECT_DIR$/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
+ 180
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/SimpleService.iml b/Chapter 02 - Services/SimpleService/SimpleService.iml
new file mode 100644
index 0000000..ace71b8
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/SimpleService.iml
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/app/app.iml b/Chapter 02 - Services/SimpleService/app/app.iml
new file mode 100644
index 0000000..c0bbed0
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/app/app.iml
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Chapter 02 - Services/SimpleService/app/build.gradle b/Chapter 02 - Services/SimpleService/app/build.gradle
new file mode 100644
index 0000000..39c4258
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/app/build.gradle
@@ -0,0 +1,21 @@
+apply plugin: 'com.android.application'
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "com.advancedandroidbook.simpleservice"
+ minSdkVersion 16
+ targetSdkVersion 21
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+dependencies {
+ compile 'com.android.support:support-v4:21.0.3'
+}
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/app/src/main/AndroidManifest.xml b/Chapter 02 - Services/SimpleService/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ae4008b
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXPoint.aidl b/Chapter 02 - Services/SimpleService/app/src/main/aidl/com/advancedandroidbook/simpleservice/GPXPoint.aidl
similarity index 100%
rename from Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXPoint.aidl
rename to Chapter 02 - Services/SimpleService/app/src/main/aidl/com/advancedandroidbook/simpleservice/GPXPoint.aidl
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl b/Chapter 02 - Services/SimpleService/app/src/main/aidl/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
similarity index 100%
rename from Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
rename to Chapter 02 - Services/SimpleService/app/src/main/aidl/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXPoint.java b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXPoint.java
similarity index 100%
rename from Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXPoint.java
rename to Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXPoint.java
diff --git a/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
new file mode 100644
index 0000000..9215bba
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
@@ -0,0 +1,197 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.Date;
+import java.util.Locale;
+
+public class GPXService extends Service {
+ private static final int GPS_NOTIFY = 0x2001;
+ private static final String DEBUG_TAG = "GPXService";
+ public static final String EXTRA_UPDATE_RATE = "update-rate";
+ public static final String GPX_SERVICE = "com.advancedandroidbook.GPXService.SERVICE";
+ private LocationManager location = null;
+ private NotificationManager notifier = null;
+ private int updateRate = -1;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ notifier = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // Android 2.0, L5, version
+ Log.v(DEBUG_TAG, "onStartCommand() called, must be on L5 or later");
+ if (flags != 0) {
+ Log.w(DEBUG_TAG, "Redelivered or retrying service start: " + flags);
+ }
+ doServiceStart(intent, startId);
+ return Service.START_REDELIVER_INTENT;
+ }
+
+ private void doServiceStart(Intent intent, int startId) {
+ updateRate = intent.getIntExtra(EXTRA_UPDATE_RATE, -1);
+ if (updateRate == -1) {
+ updateRate = 60000;
+ }
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.NO_REQUIREMENT);
+ criteria.setPowerRequirement(Criteria.POWER_LOW);
+ location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ String best = location.getBestProvider(criteria, true);
+ location.requestLocationUpdates(best, updateRate, 0, trackListener);
+ // notify that we've started up
+ Intent toLaunch = new Intent(getApplicationContext(),
+ ServiceControlActivity.class);
+ PendingIntent intentBack = PendingIntent.getActivity(
+ getApplicationContext(), 0, toLaunch, 0);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
+ builder.setTicker("Builder GPS Tracking");
+ builder.setSmallIcon(android.R.drawable.stat_notify_more);
+ builder.setWhen(System.currentTimeMillis());
+ builder.setContentTitle("Builder GPS Tracking");
+ builder.setContentText("Tracking start at " + updateRate + "ms intervals with ["
+ + best + "] as the provider.");
+ builder.setContentIntent(intentBack);
+ builder.setAutoCancel(true);
+ Notification notify = builder.build();
+
+ notifier.notify(GPS_NOTIFY, notify);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.v(DEBUG_TAG, "onDestroy() called");
+ if (location != null) {
+ location.removeUpdates(trackListener);
+ location = null;
+ }
+ // notify that we've stopped
+ Intent toLaunch = new Intent(getApplicationContext(),
+ ServiceControlActivity.class);
+ PendingIntent intentBack = PendingIntent.getActivity(
+ getApplicationContext(), 0, toLaunch, 0);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
+ builder.setTicker("Builder GPS Tracking");
+ builder.setSmallIcon(android.R.drawable.stat_notify_more);
+ builder.setWhen(System.currentTimeMillis());
+ builder.setContentTitle("Builder GPS Tracking");
+ builder.setContentText("Tracking stopped");
+ builder.setContentIntent(intentBack);
+ builder.setAutoCancel(true);
+ Notification notify = builder.build();
+
+ notifier.notify(GPS_NOTIFY, notify);
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // we only have one, so no need to check the intent
+ return mRemoteInterfaceBinder;
+ }
+
+ // our remote interface
+ private final IRemoteInterface.Stub mRemoteInterfaceBinder = new IRemoteInterface.Stub() {
+ public Location getLastLocation() {
+ Log.v("interface", "getLastLocation() called");
+ return lastLocation;
+ }
+
+ public GPXPoint getGPXPoint() {
+ if (lastLocation == null) {
+ return null;
+ } else {
+ Log.v("interface", "getGPXPoint() called");
+ GPXPoint point = new GPXPoint();
+ point.elevation = lastLocation.getAltitude();
+ point.latitude = (int) (lastLocation.getLatitude() * 1E6);
+ point.longitude = (int) (lastLocation.getLongitude() * 1E6);
+ point.timestamp = new Date(lastLocation.getTime());
+ return point;
+ }
+ }
+ };
+
+ private Location firstLocation = null;
+ private Location lastLocation = null;
+ private long lastTime = -1;
+ private long firstTime = -1;
+
+ private LocationListener trackListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ long thisTime = System.currentTimeMillis();
+ long diffTime = thisTime - lastTime;
+ Log.v(DEBUG_TAG, "diffTime == " + diffTime + ", updateRate = "
+ + updateRate);
+ if (diffTime < updateRate) {
+ // it hasn't been long enough yet
+ return;
+ }
+ lastTime = thisTime;
+ String locInfo = String.format(
+ Locale.getDefault(),
+ "Current loc = (%f, %f) @ (%.1f meters up)",
+ location.getLatitude(), location.getLongitude(),
+ location.getAltitude());
+ if (lastLocation != null) {
+ float distance = location.distanceTo(lastLocation);
+ locInfo += String.format("\n Distance from last = %.1f meters",
+ distance);
+ float lastSpeed = distance / diffTime;
+ locInfo += String.format("\n\tSpeed: %.1fm/s", lastSpeed);
+ if (location.hasSpeed()) {
+ float gpsSpeed = location.getSpeed();
+ locInfo += String.format(" (or %.1fm/s)", lastSpeed,
+ gpsSpeed);
+ } else {
+ }
+ }
+ if (firstLocation != null && firstTime != -1) {
+ float overallDistance = location.distanceTo(firstLocation);
+ float overallSpeed = overallDistance / (thisTime - firstTime);
+ locInfo += String.format(
+ "\n\tOverall speed: %.1fm/s over %.1f meters",
+ overallSpeed, overallDistance);
+ }
+ lastLocation = location;
+ if (firstLocation == null) {
+ firstLocation = location;
+ firstTime = thisTime;
+ }
+ Toast.makeText(getApplicationContext(), locInfo, Toast.LENGTH_LONG)
+ .show();
+ Log.v(DEBUG_TAG, "Test time");
+ }
+
+ public void onProviderDisabled(String provider) {
+ Log.v(DEBUG_TAG, "Provider disabled " + provider);
+ }
+
+ public void onProviderEnabled(String provider) {
+ Log.v(DEBUG_TAG, "Provider enabled " + provider);
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+}
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/MenuActivity.java b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/MenuActivity.java
similarity index 100%
rename from Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/MenuActivity.java
rename to Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/MenuActivity.java
diff --git a/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
new file mode 100644
index 0000000..e60da57
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
@@ -0,0 +1,109 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class ServiceControlActivity extends Activity implements ServiceConnection {
+ IRemoteInterface mRemoteInterface = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.control);
+
+ final TextView status = (TextView) findViewById(R.id.status);
+
+ Button go = (Button) findViewById(R.id.go);
+ go.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent service = new Intent(GPXService.GPX_SERVICE);
+ service.setPackage("com.advancedandroidbook.simpleservice");
+ service.putExtra(GPXService.EXTRA_UPDATE_RATE, 5000);
+ startService(service);
+ }
+ });
+
+ Button stop = (Button) findViewById(R.id.stop);
+ stop.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ releaseBind();
+ stopService(new Intent(GPXService.GPX_SERVICE));
+ }
+ });
+
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ String info = "Info from remote: \n";
+ Location loc = mRemoteInterface.getLastLocation();
+ if (loc != null) {
+ double lat = loc.getLatitude();
+ double lon = loc.getLongitude();
+ info += String.format("Last location = (%f, %f)\n",
+ lat, lon);
+ } else {
+ info += "No last location yet.\n";
+ }
+ GPXPoint point = mRemoteInterface.getGPXPoint();
+ if (point != null) {
+ info += String
+ .format("GPX point = (%d, %d) @ (%.1f meters) @ (%s)\n",
+ point.latitude, point.longitude,
+ point.elevation,
+ point.timestamp.toString());
+ }
+ status.setText(info);
+ } catch (RemoteException e) {
+ Log.e("ServiceControl", "Call to remote interface failed.", e);
+ }
+ }
+ });
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mRemoteInterface = IRemoteInterface.Stub.asInterface(service);
+ Log.v("ServiceControl", "Interface bound.");
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setVisibility(View.VISIBLE);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mRemoteInterface = null;
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setVisibility(View.GONE);
+ Log.v("ServiceControl", "Remote interface no longer bound");
+ }
+
+ public void releaseBind() {
+ unbindService(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // get a link to our remote service
+ Intent intent = new Intent(IRemoteInterface.class.getName());
+ intent.setPackage("com.advancedandroidbook.simpleservice");
+
+ bindService(intent, this, Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onPause() {
+ // remove the link to the remote service
+// releaseBind();
+ super.onPause();
+ }
+}
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java b/Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java
similarity index 100%
rename from Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java
rename to Chapter 02 - Services/SimpleService/app/src/main/java/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java
diff --git a/Chapter 02 - Services/SimpleService/res/drawable-hdpi/icon.png b/Chapter 02 - Services/SimpleService/app/src/main/res/drawable-hdpi/icon.png
similarity index 100%
rename from Chapter 02 - Services/SimpleService/res/drawable-hdpi/icon.png
rename to Chapter 02 - Services/SimpleService/app/src/main/res/drawable-hdpi/icon.png
diff --git a/Chapter 02 - Services/SimpleService/res/drawable-ldpi/icon.png b/Chapter 02 - Services/SimpleService/app/src/main/res/drawable-ldpi/icon.png
similarity index 100%
rename from Chapter 02 - Services/SimpleService/res/drawable-ldpi/icon.png
rename to Chapter 02 - Services/SimpleService/app/src/main/res/drawable-ldpi/icon.png
diff --git a/Chapter 02 - Services/SimpleService/res/drawable-mdpi/icon.png b/Chapter 02 - Services/SimpleService/app/src/main/res/drawable-mdpi/icon.png
similarity index 100%
rename from Chapter 02 - Services/SimpleService/res/drawable-mdpi/icon.png
rename to Chapter 02 - Services/SimpleService/app/src/main/res/drawable-mdpi/icon.png
diff --git a/Chapter 02 - Services/SimpleService/res/layout/control.xml b/Chapter 02 - Services/SimpleService/app/src/main/res/layout/control.xml
similarity index 100%
rename from Chapter 02 - Services/SimpleService/res/layout/control.xml
rename to Chapter 02 - Services/SimpleService/app/src/main/res/layout/control.xml
diff --git a/Chapter 02 - Services/SimpleService/res/values/strings.xml b/Chapter 02 - Services/SimpleService/app/src/main/res/values/strings.xml
similarity index 100%
rename from Chapter 02 - Services/SimpleService/res/values/strings.xml
rename to Chapter 02 - Services/SimpleService/app/src/main/res/values/strings.xml
diff --git a/Chapter 02 - Services/SimpleService/build.gradle b/Chapter 02 - Services/SimpleService/build.gradle
new file mode 100644
index 0000000..435d1da
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/build.gradle
@@ -0,0 +1,14 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.1.0'
+ }
+}
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
\ No newline at end of file
diff --git a/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.jar b/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.properties b/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0c71e76
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/Chapter 02 - Services/SimpleService/gradlew b/Chapter 02 - Services/SimpleService/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Chapter 02 - Services/SimpleService/gradlew.bat b/Chapter 02 - Services/SimpleService/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Chapter 02 - Services/SimpleService/import-summary.txt b/Chapter 02 - Services/SimpleService/import-summary.txt
new file mode 100644
index 0000000..ff71d91
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/import-summary.txt
@@ -0,0 +1,109 @@
+ECLIPSE ANDROID PROJECT IMPORT SUMMARY
+======================================
+
+Risky Project Location:
+-----------------------
+The tools *should* handle project locations in any directory. However,
+due to bugs, placing projects in directories containing spaces in the
+path, or characters like ", ' and &, have had issues. We're working to
+eliminate these bugs, but to save yourself headaches you may want to
+move your project to a location where this is not a problem.
+D:\git\BOOKS\advancedAndroid4e\Chapter 02 - Services\SimpleService
+ - - -
+
+Ignored Files:
+--------------
+The following files were *not* copied into the new Gradle project; you
+should evaluate whether these are still needed in your project and if
+so manually move them:
+
+* .idea\
+* .idea\.name
+* .idea\compiler.xml
+* .idea\copyright\
+* .idea\copyright\profiles_settings.xml
+* .idea\encodings.xml
+* .idea\findbugs-idea.xml
+* .idea\misc.xml
+* .idea\modules.xml
+* .idea\scopes\
+* .idea\scopes\scope_settings.xml
+* .idea\vcs.xml
+* .idea\workspace.xml
+* app\
+* app\build.gradle
+* app\src\
+* app\src\main\
+* app\src\main\AndroidManifest.xml
+* app\src\main\aidl\
+* app\src\main\aidl\com\
+* app\src\main\aidl\com\advancedandroidbook\
+* app\src\main\aidl\com\advancedandroidbook\simpleservice\
+* app\src\main\aidl\com\advancedandroidbook\simpleservice\GPXPoint.aidl
+* app\src\main\aidl\com\advancedandroidbook\simpleservice\IRemoteInterface.aidl
+* app\src\main\java\
+* app\src\main\java\com\
+* app\src\main\java\com\advancedandroidbook\
+* app\src\main\java\com\advancedandroidbook\simpleservice\
+* app\src\main\java\com\advancedandroidbook\simpleservice\GPXPoint.java
+* app\src\main\java\com\advancedandroidbook\simpleservice\GPXService.java
+* app\src\main\java\com\advancedandroidbook\simpleservice\MenuActivity.java
+* app\src\main\java\com\advancedandroidbook\simpleservice\ServiceControlActivity.java
+* app\src\main\java\com\advancedandroidbook\simpleservice\SimpleServiceActivity.java
+* app\src\main\res\
+* app\src\main\res\drawable-hdpi\
+* app\src\main\res\drawable-hdpi\icon.png
+* app\src\main\res\drawable-ldpi\
+* app\src\main\res\drawable-ldpi\icon.png
+* app\src\main\res\drawable-mdpi\
+* app\src\main\res\drawable-mdpi\icon.png
+* app\src\main\res\layout\
+* app\src\main\res\layout\control.xml
+* app\src\main\res\values\
+* app\src\main\res\values\strings.xml
+* build.gradle
+* gradle\
+* gradle\wrapper\
+* gradle\wrapper\gradle-wrapper.jar
+* gradle\wrapper\gradle-wrapper.properties
+* gradlew
+* gradlew.bat
+* settings.gradle
+
+Replaced Jars with Dependencies:
+--------------------------------
+The importer recognized the following .jar files as third party
+libraries and replaced them with Gradle dependencies instead. This has
+the advantage that more explicit version information is known, and the
+libraries can be updated automatically. However, it is possible that
+the .jar file in your project was of an older version than the
+dependency we picked, which could render the project not compileable.
+You can disable the jar replacement in the import wizard and try again:
+
+android-support-v4.jar => com.android.support:support-v4:19.1.0
+
+Moved Files:
+------------
+Android Gradle projects use a different directory structure than ADT
+Eclipse projects. Here's how the projects were restructured:
+
+* AndroidManifest.xml => app\src\main\AndroidManifest.xml
+* res\ => app\src\main\res\
+* src\ => app\src\main\java\
+* src\com\advancedandroidbook\simpleservice\GPXPoint.aidl => app\src\main\aidl\com\advancedandroidbook\simpleservice\GPXPoint.aidl
+* src\com\advancedandroidbook\simpleservice\IRemoteInterface.aidl => app\src\main\aidl\com\advancedandroidbook\simpleservice\IRemoteInterface.aidl
+
+Next Steps:
+-----------
+You can now build the project. The Gradle project needs network
+connectivity to download dependencies.
+
+Bugs:
+-----
+If for some reason your project does not build, and you determine that
+it is due to a bug or limitation of the Eclipse to Gradle importer,
+please file a bug at http://b.android.com with category
+Component-Tools.
+
+(This import summary is for your information only, and can be deleted
+after import once you are satisfied with the results.)
diff --git a/Chapter 02 - Services/SimpleService/libs/android-support-v4.jar b/Chapter 02 - Services/SimpleService/libs/android-support-v4.jar
deleted file mode 100644
index 9056828..0000000
Binary files a/Chapter 02 - Services/SimpleService/libs/android-support-v4.jar and /dev/null differ
diff --git a/Chapter 02 - Services/SimpleService/project.properties b/Chapter 02 - Services/SimpleService/project.properties
deleted file mode 100644
index 4ab1256..0000000
--- a/Chapter 02 - Services/SimpleService/project.properties
+++ /dev/null
@@ -1,14 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
diff --git a/Chapter 02 - Services/SimpleService/settings.gradle b/Chapter 02 - Services/SimpleService/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Chapter 02 - Services/SimpleService/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXService.java b/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXService.java
deleted file mode 100644
index 3af9806..0000000
--- a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/GPXService.java
+++ /dev/null
@@ -1,206 +0,0 @@
-package com.advancedandroidbook.simpleservice;
-
-import java.util.Date;
-import java.util.Locale;
-
-import com.advancedandroidbook.simpleservice.IRemoteInterface;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.content.Context;
-import android.content.Intent;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.support.v4.app.NotificationCompat;
-import android.util.Log;
-import android.widget.Toast;
-
-public class GPXService extends Service {
- private static final int GPS_NOTIFY = 0x2001;
- private static final String DEBUG_TAG = "GPXService";
- public static final String EXTRA_UPDATE_RATE = "update-rate";
- public static final String GPX_SERVICE = "com.advancedandroidbook.GPXService.SERVICE";
- private LocationManager location = null;
- private NotificationManager notifier = null;
- private int updateRate = -1;
-
- @Override
- public void onCreate() {
- super.onCreate();
- location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- notifier = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- // Android 2.0, L5, version
- Log.v(DEBUG_TAG, "onStartCommand() called, must be on L5 or later");
- if (flags != 0) {
- Log.w(DEBUG_TAG, "Redelivered or retrying service start: " + flags);
- }
- doServiceStart(intent, startId);
- return Service.START_REDELIVER_INTENT;
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- // Pre Android 2.0 version
- super.onStart(intent, startId);
- Log.v(DEBUG_TAG, "onStart() called, must be on L3 or L4");
- doServiceStart(intent, startId);
- }
-
- private void doServiceStart(Intent intent, int startId) {
- updateRate = intent.getIntExtra(EXTRA_UPDATE_RATE, -1);
- if (updateRate == -1) {
- updateRate = 60000;
- }
- Criteria criteria = new Criteria();
- criteria.setAccuracy(Criteria.NO_REQUIREMENT);
- criteria.setPowerRequirement(Criteria.POWER_LOW);
- location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- String best = location.getBestProvider(criteria, true);
- location.requestLocationUpdates(best, updateRate, 0, trackListener);
- // notify that we've started up
- Intent toLaunch = new Intent(getApplicationContext(),
- ServiceControlActivity.class);
- PendingIntent intentBack = PendingIntent.getActivity(
- getApplicationContext(), 0, toLaunch, 0);
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
- builder.setTicker("Builder GPS Tracking");
- builder.setSmallIcon(android.R.drawable.stat_notify_more);
- builder.setWhen(System.currentTimeMillis());
- builder.setContentTitle("Builder GPS Tracking");
- builder.setContentText("Tracking start at " + updateRate + "ms intervals with ["
- + best + "] as the provider.");
- builder.setContentIntent(intentBack);
- builder.setAutoCancel(true);
- Notification notify = builder.build();
-
- notifier.notify(GPS_NOTIFY, notify);
- }
-
- @Override
- public void onDestroy() {
- Log.v(DEBUG_TAG, "onDestroy() called");
- if (location != null) {
- location.removeUpdates(trackListener);
- location = null;
- }
- // notify that we've stopped
- Intent toLaunch = new Intent(getApplicationContext(),
- ServiceControlActivity.class);
- PendingIntent intentBack = PendingIntent.getActivity(
- getApplicationContext(), 0, toLaunch, 0);
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
- builder.setTicker("Builder GPS Tracking");
- builder.setSmallIcon(android.R.drawable.stat_notify_more);
- builder.setWhen(System.currentTimeMillis());
- builder.setContentTitle("Builder GPS Tracking");
- builder.setContentText("Tracking stopped");
- builder.setContentIntent(intentBack);
- builder.setAutoCancel(true);
- Notification notify = builder.build();
-
- notifier.notify(GPS_NOTIFY, notify);
- super.onDestroy();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- // we only have one, so no need to check the intent
- return mRemoteInterfaceBinder;
- }
-
- // our remote interface
- private final IRemoteInterface.Stub mRemoteInterfaceBinder = new IRemoteInterface.Stub() {
- public Location getLastLocation() {
- Log.v("interface", "getLastLocation() called");
- return lastLocation;
- }
-
- public GPXPoint getGPXPoint() {
- if (lastLocation == null) {
- return null;
- } else {
- Log.v("interface", "getGPXPoint() called");
- GPXPoint point = new GPXPoint();
- point.elevation = lastLocation.getAltitude();
- point.latitude = (int) (lastLocation.getLatitude() * 1E6);
- point.longitude = (int) (lastLocation.getLongitude() * 1E6);
- point.timestamp = new Date(lastLocation.getTime());
- return point;
- }
- }
- };
-
- private Location firstLocation = null;
- private Location lastLocation = null;
- private long lastTime = -1;
- private long firstTime = -1;
-
- private LocationListener trackListener = new LocationListener() {
- public void onLocationChanged(Location location) {
- long thisTime = System.currentTimeMillis();
- long diffTime = thisTime - lastTime;
- Log.v(DEBUG_TAG, "diffTime == " + diffTime + ", updateRate = "
- + updateRate);
- if (diffTime < updateRate) {
- // it hasn't been long enough yet
- return;
- }
- lastTime = thisTime;
- String locInfo = String.format(
- Locale.getDefault(),
- "Current loc = (%f, %f) @ (%.1f meters up)",
- location.getLatitude(), location.getLongitude(),
- location.getAltitude());
- if (lastLocation != null) {
- float distance = location.distanceTo(lastLocation);
- locInfo += String.format("\n Distance from last = %.1f meters",
- distance);
- float lastSpeed = distance / diffTime;
- locInfo += String.format("\n\tSpeed: %.1fm/s", lastSpeed);
- if (location.hasSpeed()) {
- float gpsSpeed = location.getSpeed();
- locInfo += String.format(" (or %.1fm/s)", lastSpeed,
- gpsSpeed);
- } else {
- }
- }
- if (firstLocation != null && firstTime != -1) {
- float overallDistance = location.distanceTo(firstLocation);
- float overallSpeed = overallDistance / (thisTime - firstTime);
- locInfo += String.format(
- "\n\tOverall speed: %.1fm/s over %.1f meters",
- overallSpeed, overallDistance);
- }
- lastLocation = location;
- if (firstLocation == null) {
- firstLocation = location;
- firstTime = thisTime;
- }
- Toast.makeText(getApplicationContext(), locInfo, Toast.LENGTH_LONG)
- .show();
- Log.v(DEBUG_TAG, "Test time");
- }
-
- public void onProviderDisabled(String provider) {
- Log.v(DEBUG_TAG, "Provider disabled " + provider);
- }
-
- public void onProviderEnabled(String provider) {
- Log.v(DEBUG_TAG, "Provider enabled " + provider);
- }
-
- public void onStatusChanged(String provider, int status, Bundle extras) {}
- };
-}
diff --git a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/ServiceControlActivity.java b/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
deleted file mode 100644
index 2119712..0000000
--- a/Chapter 02 - Services/SimpleService/src/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.advancedandroidbook.simpleservice;
-
-import com.advancedandroidbook.simpleservice.IRemoteInterface;
-
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.location.Location;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-import android.widget.TextView;
-
-public class ServiceControlActivity extends Activity implements
- ServiceConnection {
- IRemoteInterface mRemoteInterface = null;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.control);
-
- final TextView status = (TextView) findViewById(R.id.status);
-
- Button go = (Button) findViewById(R.id.go);
- go.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- Intent service = new Intent(GPXService.GPX_SERVICE);
- service.putExtra(GPXService.EXTRA_UPDATE_RATE, 5000);
- startService(service);
- }
- });
-
- Button stop = (Button) findViewById(R.id.stop);
- stop.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- releaseBind();
- stopService(new Intent(GPXService.GPX_SERVICE));
- }
- });
-
- Button getLastLoc = (Button) findViewById(R.id.get_last);
- getLastLoc.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- try {
- String info = "Info from remote: \n";
- Location loc = mRemoteInterface.getLastLocation();
- if (loc != null) {
- double lat = loc.getLatitude();
- double lon = loc.getLongitude();
- info += String.format("Last location = (%f, %f)\n",
- lat, lon);
- } else {
- info += "No last location yet.\n";
- }
- GPXPoint point = mRemoteInterface.getGPXPoint();
- if (point != null) {
- info += String
- .format("GPX point = (%d, %d) @ (%.1f meters) @ (%s)\n",
- point.latitude, point.longitude,
- point.elevation,
- point.timestamp.toString());
- }
- status.setText(info);
- } catch (RemoteException e) {
- Log.e("ServiceControl", "Call to remote interface failed.", e);
- }
- }
- });
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- mRemoteInterface = IRemoteInterface.Stub.asInterface(service);
- Log.v("ServiceControl", "Interface bound.");
- Button getLastLoc = (Button) findViewById(R.id.get_last);
- getLastLoc.setVisibility(View.VISIBLE);
- }
-
- public void onServiceDisconnected(ComponentName name) {
- mRemoteInterface = null;
- Button getLastLoc = (Button) findViewById(R.id.get_last);
- getLastLoc.setVisibility(View.GONE);
- Log.v("ServiceControl", "Remote interface no longer bound");
- }
-
- public void releaseBind() {
- unbindService(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- // get a link to our remote service
- bindService(new Intent(IRemoteInterface.class.getName()), this,
- Context.BIND_AUTO_CREATE);
- }
-
- @Override
- protected void onPause() {
- // remove the link to the remote service
-// releaseBind();
- super.onPause();
- }
-}
\ No newline at end of file
diff --git a/Chapter 02 - Services/UseService/.gitignore b/Chapter 02 - Services/UseService/.gitignore
new file mode 100644
index 0000000..03eee2a
--- /dev/null
+++ b/Chapter 02 - Services/UseService/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+.idea
+*.iml
+.DS_Store
+build
diff --git a/Chapter 02 - Services/UseService/AndroidManifest.xml b/Chapter 02 - Services/UseService/AndroidManifest.xml
deleted file mode 100644
index 56141b2..0000000
--- a/Chapter 02 - Services/UseService/AndroidManifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Chapter 02 - Services/UseService/app/build.gradle b/Chapter 02 - Services/UseService/app/build.gradle
new file mode 100644
index 0000000..dadc4d4
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/build.gradle
@@ -0,0 +1,23 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion "21.1.2"
+
+ defaultConfig {
+ applicationId "com.advancedandroidbook.simpleservice"
+ minSdkVersion 16
+ targetSdkVersion 21
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:21.0.3'
+}
diff --git a/Chapter 02 - Services/SimpleService/AndroidManifest.xml b/Chapter 02 - Services/UseService/app/src/main/AndroidManifest.xml
similarity index 81%
rename from Chapter 02 - Services/SimpleService/AndroidManifest.xml
rename to Chapter 02 - Services/UseService/app/src/main/AndroidManifest.xml
index 84d1906..1768f1c 100644
--- a/Chapter 02 - Services/SimpleService/AndroidManifest.xml
+++ b/Chapter 02 - Services/UseService/app/src/main/AndroidManifest.xml
@@ -2,21 +2,17 @@
-
-
+ android:versionName="1.0">
+ android:label="@string/app_name">
+ android:label="@string/app_name">
@@ -27,7 +23,7 @@
+ android:enabled="true">
diff --git a/Chapter 02 - Services/UseService/src/com/advancedandroidbook/simpleservice/GPXPoint.aidl b/Chapter 02 - Services/UseService/app/src/main/aidl/com/advancedandroidbook/simpleservice/GPXPoint.aidl
similarity index 100%
rename from Chapter 02 - Services/UseService/src/com/advancedandroidbook/simpleservice/GPXPoint.aidl
rename to Chapter 02 - Services/UseService/app/src/main/aidl/com/advancedandroidbook/simpleservice/GPXPoint.aidl
diff --git a/Chapter 02 - Services/UseService/src/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl b/Chapter 02 - Services/UseService/app/src/main/aidl/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
similarity index 98%
rename from Chapter 02 - Services/UseService/src/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
rename to Chapter 02 - Services/UseService/app/src/main/aidl/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
index e0438b7..8749b17 100644
--- a/Chapter 02 - Services/UseService/src/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
+++ b/Chapter 02 - Services/UseService/app/src/main/aidl/com/advancedandroidbook/simpleservice/IRemoteInterface.aidl
@@ -2,7 +2,9 @@ package com.advancedandroidbook.simpleservice;
import com.advancedandroidbook.simpleservice.GPXPoint;
+
interface IRemoteInterface {
+
Location getLastLocation();
GPXPoint getGPXPoint();
}
diff --git a/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXPoint.java b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXPoint.java
new file mode 100644
index 0000000..725c7f3
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXPoint.java
@@ -0,0 +1,54 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Date;
+
+public final class GPXPoint implements Parcelable {
+
+ public int latitude;
+ public int longitude;
+ public Date timestamp;
+ public double elevation;
+
+ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+
+ public GPXPoint createFromParcel(Parcel src) {
+ return new GPXPoint(src);
+ }
+
+ public GPXPoint[] newArray(int size) {
+ return new GPXPoint[size];
+ }
+
+ };
+
+ public GPXPoint() {
+ }
+
+ private GPXPoint(Parcel src) {
+ readFromParcel(src);
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(latitude);
+ dest.writeInt(longitude);
+ dest.writeDouble(elevation);
+ dest.writeLong(timestamp.getTime());
+ }
+
+ public void readFromParcel(Parcel src) {
+ latitude = src.readInt();
+ longitude = src.readInt();
+
+ elevation = src.readDouble();
+ timestamp = new Date(src.readLong());
+ }
+
+ public int describeContents() {
+ // nothing special
+ return 0;
+ }
+
+}
diff --git a/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
new file mode 100644
index 0000000..e1c7bd7
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/GPXService.java
@@ -0,0 +1,205 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Criteria;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.Date;
+import java.util.Locale;
+
+public class GPXService extends Service {
+ private static final int GPS_NOTIFY = 0x2001;
+ private static final String DEBUG_TAG = "GPXService";
+ public static final String EXTRA_UPDATE_RATE = "update-rate";
+ public static final String GPX_SERVICE = "com.advancedandroidbook.GPXService.SERVICE";
+ private LocationManager location = null;
+ private NotificationManager notifier = null;
+ private int updateRate = -1;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ notifier = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ // Android 2.0, L5, version
+ Log.v(DEBUG_TAG, "onStartCommand() called, must be on L5 or later");
+ if (flags != 0) {
+ Log.w(DEBUG_TAG, "Redelivered or retrying service start: " + flags);
+ }
+ doServiceStart(intent, startId);
+ return Service.START_REDELIVER_INTENT;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ // Pre Android 2.0 version
+ super.onStart(intent, startId);
+ Log.v(DEBUG_TAG, "onStart() called, must be on L3 or L4");
+ doServiceStart(intent, startId);
+ }
+
+ private void doServiceStart(Intent intent, int startId) {
+ updateRate = intent.getIntExtra(EXTRA_UPDATE_RATE, -1);
+ if (updateRate == -1) {
+ updateRate = 60000;
+ }
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.NO_REQUIREMENT);
+ criteria.setPowerRequirement(Criteria.POWER_LOW);
+ location = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+ String best = location.getBestProvider(criteria, true);
+ location.requestLocationUpdates(best, updateRate, 0, trackListener);
+ // notify that we've started up
+ Intent toLaunch = new Intent(getApplicationContext(),
+ ServiceControlActivity.class);
+ PendingIntent intentBack = PendingIntent.getActivity(
+ getApplicationContext(), 0, toLaunch, 0);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
+ builder.setTicker("Builder GPS Tracking");
+ builder.setSmallIcon(android.R.drawable.stat_notify_more);
+ builder.setWhen(System.currentTimeMillis());
+ builder.setContentTitle("Builder GPS Tracking");
+ builder.setContentText("Tracking start at " + updateRate + "ms intervals with ["
+ + best + "] as the provider.");
+ builder.setContentIntent(intentBack);
+ builder.setAutoCancel(true);
+ Notification notify = builder.build();
+
+ notifier.notify(GPS_NOTIFY, notify);
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.v(DEBUG_TAG, "onDestroy() called");
+ if (location != null) {
+ location.removeUpdates(trackListener);
+ location = null;
+ }
+ // notify that we've stopped
+ Intent toLaunch = new Intent(getApplicationContext(),
+ ServiceControlActivity.class);
+ PendingIntent intentBack = PendingIntent.getActivity(
+ getApplicationContext(), 0, toLaunch, 0);
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
+ builder.setTicker("Builder GPS Tracking");
+ builder.setSmallIcon(android.R.drawable.stat_notify_more);
+ builder.setWhen(System.currentTimeMillis());
+ builder.setContentTitle("Builder GPS Tracking");
+ builder.setContentText("Tracking stopped");
+ builder.setContentIntent(intentBack);
+ builder.setAutoCancel(true);
+ Notification notify = builder.build();
+
+ notifier.notify(GPS_NOTIFY, notify);
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // we only have one, so no need to check the intent
+ return mRemoteInterfaceBinder;
+ }
+
+ // our remote interface
+ private final IRemoteInterface.Stub mRemoteInterfaceBinder = new IRemoteInterface.Stub() {
+ public Location getLastLocation() {
+ Log.v("interface", "getLastLocation() called");
+ return lastLocation;
+ }
+
+ public GPXPoint getGPXPoint() {
+ if (lastLocation == null) {
+ return null;
+ } else {
+ Log.v("interface", "getGPXPoint() called");
+ GPXPoint point = new GPXPoint();
+ point.elevation = lastLocation.getAltitude();
+ point.latitude = (int) (lastLocation.getLatitude() * 1E6);
+ point.longitude = (int) (lastLocation.getLongitude() * 1E6);
+ point.timestamp = new Date(lastLocation.getTime());
+ return point;
+ }
+ }
+ };
+
+ private Location firstLocation = null;
+ private Location lastLocation = null;
+ private long lastTime = -1;
+ private long firstTime = -1;
+
+ private LocationListener trackListener = new LocationListener() {
+ public void onLocationChanged(Location location) {
+ long thisTime = System.currentTimeMillis();
+ long diffTime = thisTime - lastTime;
+ Log.v(DEBUG_TAG, "diffTime == " + diffTime + ", updateRate = "
+ + updateRate);
+ if (diffTime < updateRate) {
+ // it hasn't been long enough yet
+ return;
+ }
+ lastTime = thisTime;
+ String locInfo = String.format(
+ Locale.getDefault(),
+ "Current loc = (%f, %f) @ (%.1f meters up)",
+ location.getLatitude(), location.getLongitude(),
+ location.getAltitude());
+ if (lastLocation != null) {
+ float distance = location.distanceTo(lastLocation);
+ locInfo += String.format("\n Distance from last = %.1f meters",
+ distance);
+ float lastSpeed = distance / diffTime;
+ locInfo += String.format("\n\tSpeed: %.1fm/s", lastSpeed);
+ if (location.hasSpeed()) {
+ float gpsSpeed = location.getSpeed();
+ locInfo += String.format(" (or %.1fm/s)", lastSpeed,
+ gpsSpeed);
+ } else {
+ }
+ }
+ if (firstLocation != null && firstTime != -1) {
+ float overallDistance = location.distanceTo(firstLocation);
+ float overallSpeed = overallDistance / (thisTime - firstTime);
+ locInfo += String.format(
+ "\n\tOverall speed: %.1fm/s over %.1f meters",
+ overallSpeed, overallDistance);
+ }
+ lastLocation = location;
+ if (firstLocation == null) {
+ firstLocation = location;
+ firstTime = thisTime;
+ }
+ Toast.makeText(getApplicationContext(), locInfo, Toast.LENGTH_LONG)
+ .show();
+ Log.v(DEBUG_TAG, "Test time");
+ }
+
+ public void onProviderDisabled(String provider) {
+ Log.v(DEBUG_TAG, "Provider disabled " + provider);
+ }
+
+ public void onProviderEnabled(String provider) {
+ Log.v(DEBUG_TAG, "Provider enabled " + provider);
+ }
+
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ }
+ };
+}
diff --git a/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/MenuActivity.java b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/MenuActivity.java
new file mode 100644
index 0000000..a904c02
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/MenuActivity.java
@@ -0,0 +1,43 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+public abstract class MenuActivity extends ListActivity {
+ private SortedMap actions = new TreeMap();
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ String key = (String) l.getItemAtPosition(position);
+ startActivity((Intent) actions.get(key));
+ }
+
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ prepareMenu();
+
+ String[] keys = actions.keySet().toArray(
+ new String[actions.keySet().size()]);
+
+ setListAdapter(new ArrayAdapter(this,
+ android.R.layout.simple_list_item_1, keys));
+ }
+
+ public void addMenuItem(String label, Class> cls) {
+ actions.put(label, new Intent(this, cls));
+ }
+
+ abstract void prepareMenu();
+}
diff --git a/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
new file mode 100644
index 0000000..1913afe
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/ServiceControlActivity.java
@@ -0,0 +1,118 @@
+package com.advancedandroidbook.simpleservice;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.location.Location;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class ServiceControlActivity extends Activity implements
+ ServiceConnection {
+ IRemoteInterface mRemoteInterface = null;
+
+ // Flag to keep track of bound status
+ private boolean mIsBound;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.control);
+
+ final TextView status = (TextView) findViewById(R.id.status);
+
+ Button go = (Button) findViewById(R.id.go);
+ go.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent service = new Intent(GPXService.GPX_SERVICE);
+ service.setPackage("com.advancedandroidbook.simpleservice");
+ service.putExtra(GPXService.EXTRA_UPDATE_RATE, 5000);
+ startService(service);
+ }
+ });
+
+ Button stop = (Button) findViewById(R.id.stop);
+ stop.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ releaseBind();
+ Intent intent = new Intent(GPXService.GPX_SERVICE);
+ intent.setPackage("com.advancedandroidbook.simpleservice");
+ stopService(intent);
+ }
+ });
+
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ try {
+ String info = "Info from remote: \n";
+ Location loc = mRemoteInterface.getLastLocation();
+ if (loc != null) {
+ double lat = loc.getLatitude();
+ double lon = loc.getLongitude();
+ info += String.format("Last location = (%f, %f)\n",
+ lat, lon);
+ } else {
+ info += "No last location yet.\n";
+ }
+ GPXPoint point = mRemoteInterface.getGPXPoint();
+ if (point != null) {
+ info += String
+ .format("GPX point = (%d, %d) @ (%.1f meters) @ (%s)\n",
+ point.latitude, point.longitude,
+ point.elevation,
+ point.timestamp.toString());
+ }
+ status.setText(info);
+ } catch (RemoteException e) {
+ Log.e("ServiceControl", "Call to remote interface failed.", e);
+ }
+ }
+ });
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mRemoteInterface = IRemoteInterface.Stub.asInterface(service);
+ Log.v("ServiceControl", "Interface bound.");
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setVisibility(View.VISIBLE);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ mRemoteInterface = null;
+ Button getLastLoc = (Button) findViewById(R.id.get_last);
+ getLastLoc.setVisibility(View.GONE);
+ Log.v("ServiceControl", "Remote interface no longer bound");
+ }
+
+ public void releaseBind() {
+ if (mIsBound) {
+ unbindService(this);
+ mIsBound = false;
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ // get a link to our remote service
+ Intent intent = new Intent(IRemoteInterface.class.getName());
+ intent.setPackage("com.advancedandroidbook.simpleservice");
+ bindService(intent, this, Context.BIND_AUTO_CREATE);
+ mIsBound = true;
+ }
+
+ @Override
+ protected void onPause() {
+ // remove the link to the remote service
+// releaseBind();
+ super.onPause();
+ }
+}
\ No newline at end of file
diff --git a/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java
new file mode 100644
index 0000000..da6923d
--- /dev/null
+++ b/Chapter 02 - Services/UseService/app/src/main/java/com/advancedandroidbook/simpleservice/SimpleServiceActivity.java
@@ -0,0 +1,8 @@
+package com.advancedandroidbook.simpleservice;
+
+public class SimpleServiceActivity extends MenuActivity {
+ @Override
+ void prepareMenu() {
+ addMenuItem("1. Service Control", ServiceControlActivity.class);
+ }
+}
\ No newline at end of file
diff --git a/Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-hdpi/icon.png b/Chapter 02 - Services/UseService/app/src/main/res/drawable-hdpi/icon.png
similarity index 100%
rename from Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-hdpi/icon.png
rename to Chapter 02 - Services/UseService/app/src/main/res/drawable-hdpi/icon.png
diff --git a/Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-ldpi/icon.png b/Chapter 02 - Services/UseService/app/src/main/res/drawable-ldpi/icon.png
similarity index 100%
rename from Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-ldpi/icon.png
rename to Chapter 02 - Services/UseService/app/src/main/res/drawable-ldpi/icon.png
diff --git a/Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-mdpi/icon.png b/Chapter 02 - Services/UseService/app/src/main/res/drawable-mdpi/icon.png
similarity index 100%
rename from Chapter 16 - Hardware APIs/SimpleBluetooth/res/drawable-mdpi/icon.png
rename to Chapter 02 - Services/UseService/app/src/main/res/drawable-mdpi/icon.png
diff --git a/Chapter 02 - Services/UseService/res/layout/main.xml b/Chapter 02 - Services/UseService/app/src/main/res/layout/control.xml
similarity index 90%
rename from Chapter 02 - Services/UseService/res/layout/main.xml
rename to Chapter 02 - Services/UseService/app/src/main/res/layout/control.xml
index 2c04849..c532632 100644
--- a/Chapter 02 - Services/UseService/res/layout/main.xml
+++ b/Chapter 02 - Services/UseService/app/src/main/res/layout/control.xml
@@ -2,12 +2,12 @@
+ android:orientation="vertical">
+ android:text="@string/local_use_of_remote_service" />