context = activity.getApplicationContext();
myTracksProviderUtils = MyTracksProviderUtils.Factory.get(context);
- HttpTransport transport = ApiFeatures.getInstance().getApiAdapter().getHttpTransport();
+ HttpTransport transport = ApiAdapterFactory.getApiAdapter().getHttpTransport();
httpRequestFactory = transport.createRequestFactory(new MethodOverride());
canRetry = true;
diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java
index b6f3fc8d1..da27228bf 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/io/sendtogoogle/UploadServiceChooserActivity.java
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.io.sendtogoogle;
import com.google.android.apps.mytracks.Constants;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.app.Activity;
@@ -218,7 +218,7 @@ public class UploadServiceChooserActivity extends Activity {
editor.putBoolean(getString(R.string.send_to_fusion_tables_key), sendFusionTables());
editor.putBoolean(getString(R.string.send_to_docs_key), sendDocs());
}
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
/**
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java
index 501fadf39..9e9c90a35 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/PreferenceManager.java
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.services;
import com.google.android.apps.mytracks.Constants;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.content.Context;
@@ -174,19 +174,19 @@ public class PreferenceManager implements OnSharedPreferenceChangeListener {
public void setAutoResumeTrackCurrentRetry(int retryAttempts) {
Editor editor = sharedPreferences.edit();
editor.putInt(autoResumeTrackCurrentRetryKey, retryAttempts);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
public void setRecordingTrack(long id) {
Editor editor = sharedPreferences.edit();
editor.putLong(recordingTrackKey, id);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
public void setSelectedTrack(long id) {
Editor editor = sharedPreferences.edit();
editor.putLong(selectedTrackKey, id);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
public void shutdown() {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
index ee1e021cc..8b81a44df 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/TrackRecordingService.java
@@ -36,14 +36,12 @@ import com.google.android.apps.mytracks.services.tasks.SplitTask;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
import com.google.android.apps.mytracks.stats.TripStatistics;
import com.google.android.apps.mytracks.stats.TripStatisticsBuilder;
-import com.google.android.apps.mytracks.util.ApiFeatures;
-import com.google.android.apps.mytracks.util.ApiLevelAdapter;
import com.google.android.apps.mytracks.util.LocationUtils;
import com.google.android.apps.mytracks.util.StringUtils;
import com.google.android.maps.mytracks.R;
+import com.google.common.annotations.VisibleForTesting;
import android.app.Notification;
-import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentValues;
@@ -80,7 +78,6 @@ public class TrackRecordingService extends Service {
static final int MAX_AUTO_RESUME_TRACK_RETRY_ATTEMPTS = 3;
- private NotificationManager notificationManager;
private LocationManager locationManager;
private WakeLock wakeLock;
@@ -243,8 +240,6 @@ public class TrackRecordingService extends Service {
super.onCreate();
Log.d(TAG, "TrackRecordingService.onCreate");
providerUtils = MyTracksProviderUtils.Factory.get(this);
- notificationManager =
- (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
setUpTaskExecutors();
@@ -369,7 +364,6 @@ public class TrackRecordingService extends Service {
// Make sure we have no indirect references to this service.
locationManager = null;
- notificationManager = null;
providerUtils = null;
binder.detachFromService();
binder = null;
@@ -485,8 +479,6 @@ public class TrackRecordingService extends Service {
* Shows the notification message and icon in the notification bar.
*/
private void showNotification() {
- final ApiLevelAdapter apiLevelAdapter =
- ApiFeatures.getInstance().getApiAdapter();
if (isRecording) {
Notification notification = new Notification(
R.drawable.arrow_320, null /* tickerText */,
@@ -497,16 +489,24 @@ public class TrackRecordingService extends Service {
notification.setLatestEventInfo(this, getString(R.string.my_tracks_app_name),
getString(R.string.track_record_notification), contentIntent);
notification.flags += Notification.FLAG_NO_CLEAR;
- apiLevelAdapter.startForeground(this, notificationManager, 1,
- notification);
+ startForegroundService(notification);
} else {
- apiLevelAdapter.stopForeground(this, notificationManager, 1);
+ stopForegroundService();
}
}
+ @VisibleForTesting
+ protected void startForegroundService(Notification notification) {
+ startForeground(1, notification);
+ }
+
+ @VisibleForTesting
+ protected void stopForegroundService() {
+ stopForeground(true);
+ }
+
private void setUpTaskExecutors() {
- announcementExecutor = new PeriodicTaskExecutor(
- this, new StatusAnnouncerFactory(ApiFeatures.getInstance()));
+ announcementExecutor = new PeriodicTaskExecutor(this, new StatusAnnouncerFactory());
splitExecutor = new PeriodicTaskExecutor(this, new SplitTask.Factory());
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java
index 4a3bc512b..558bfe423 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/BluetoothConnectionManager.java
@@ -18,7 +18,7 @@ package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -31,8 +31,6 @@ import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.UUID;
/**
@@ -44,8 +42,9 @@ import java.util.UUID;
* @author Sandor Dornbush
*/
public class BluetoothConnectionManager {
- // Unique UUID for this application
- private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
+
+ // Unique Bluetooth UUID for My Tracks
+ public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private MessageParser parser;
@@ -254,41 +253,13 @@ public class BluetoothConnectionManager {
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
- tmp = getSocket();
+ tmp = ApiAdapterFactory.getApiAdapter().getBluetoothSocket(device);
} catch (IOException e) {
Log.e(Constants.TAG, "create() failed", e);
}
socket = tmp;
}
- private BluetoothSocket getSocket() throws IOException {
- if (ApiFeatures.getInstance().hasBluetoothDeviceCreateInsecureRfcommSocketToServiceRecord()) {
- try {
- return device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
- } catch (IOException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- }
- } else {
- try {
- Class extends BluetoothDevice> c = device.getClass();
- Method insecure = c.getMethod("createInsecureRfcommSocket", Integer.class);
- insecure.setAccessible(true);
- return (BluetoothSocket) insecure.invoke(device, 1);
- } catch (SecurityException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- } catch (NoSuchMethodException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- } catch (IllegalArgumentException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- } catch (IllegalAccessException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- } catch (InvocationTargetException e) {
- Log.e(Constants.TAG, "Unable to get insecure connect.", e);
- }
- }
- return device.createRfcommSocketToServiceRecord(SPP_UUID);
- }
-
@Override
public void run() {
Log.d(Constants.TAG, "BEGIN mConnectThread");
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java
index b70e0fddf..a8354ee95 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ZephyrMessageParser.java
@@ -16,7 +16,7 @@
package com.google.android.apps.mytracks.services.sensors;
import com.google.android.apps.mytracks.content.Sensor;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import java.util.Arrays;
@@ -62,8 +62,7 @@ public class ZephyrMessageParser implements MessageParser {
// 0x1A00316550003162 produces erroneous values for Cadence and needs
// a workaround based on the stride counter.
// Firmware values range from field 3 to 10 (inclusive) of the byte buffer.
- byte[] hardwareFirmwareId = ApiFeatures.getInstance().getApiAdapter()
- .copyByteArray(buffer, 3, 11);
+ byte[] hardwareFirmwareId = ApiAdapterFactory.getApiAdapter().copyByteArray(buffer, 3, 11);
Sensor.SensorData.Builder cadence = Sensor.SensorData.newBuilder();
if (Arrays.equals(hardwareFirmwareId, CADENCE_BUG_FW_ID)) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java
index 4c0db0864..e72635f79 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntDirectSensorManager.java
@@ -22,7 +22,7 @@ import com.dsi.ant.AntMesg;
import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.maps.mytracks.R;
import android.content.Context;
@@ -135,7 +135,7 @@ public class AntDirectSensorManager extends AntSensorManager {
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(context.getString(R.string.ant_heart_rate_sensor_id_key), deviceNumberHRM);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
private void handleMessageResponse(byte[] rawMessage) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSrmBridgeSensorManager.java b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSrmBridgeSensorManager.java
index 1db754b4d..1006e4a6b 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSrmBridgeSensorManager.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/sensors/ant/AntSrmBridgeSensorManager.java
@@ -22,7 +22,7 @@ import com.dsi.ant.AntMesg;
import com.dsi.ant.exception.AntInterfaceException;
import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.content.Sensor;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import com.google.android.apps.mytracks.util.SystemUtils;
import com.google.android.maps.mytracks.R;
@@ -199,7 +199,7 @@ public class AntSrmBridgeSensorManager extends AntSensorManager {
Constants.SETTINGS_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(context.getString(R.string.ant_srm_bridge_sensor_id_key), deviceNumber);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
private void handleMessageResponse(byte[] rawMessage) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactory.java b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactory.java
index 739b81cb3..7e8b56b37 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactory.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactory.java
@@ -15,10 +15,9 @@
*/
package com.google.android.apps.mytracks.services.tasks;
-import com.google.android.apps.mytracks.util.ApiFeatures;
+import com.google.android.apps.mytracks.util.ApiAdapterFactory;
import android.content.Context;
-import android.media.AudioManager;
/**
* Factory which wraps construction and setup of text-to-speech announcements in
@@ -28,30 +27,11 @@ import android.media.AudioManager;
*/
public class StatusAnnouncerFactory implements PeriodicTaskFactory {
- private final boolean hasTts;
-
- public StatusAnnouncerFactory(ApiFeatures apiFeatures) {
- this.hasTts = apiFeatures.hasTextToSpeech();
+ public StatusAnnouncerFactory() {
}
@Override
public PeriodicTask create(Context context) {
- if (hasTts) {
- return ApiFeatures.getInstance().getApiAdapter().getStatusAnnouncerTask(context);
- } else {
- return null;
- }
- }
-
- /**
- * Returns the appropriate volume stream for controlling announcement
- * volume.
- */
- public int getVolumeStream() {
- if (hasTts) {
- return StatusAnnouncerTask.getVolumeStream();
- } else {
- return AudioManager.USE_DEFAULT_STREAM_TYPE;
- }
+ return ApiAdapterFactory.getApiAdapter().getStatusAnnouncerTask(context);
}
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerTask.java b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerTask.java
index f6532249b..24bbd674b 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerTask.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerTask.java
@@ -331,13 +331,6 @@ public class StatusAnnouncerTask implements PeriodicTask {
}
}
- /**
- * Returns the volume stream to use for controlling announcement volume.
- */
- public static int getVolumeStream() {
- return TextToSpeech.Engine.DEFAULT_STREAM;
- }
-
/**
* Gets a string to announce the time.
*
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/Api10Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api10Adapter.java
new file mode 100644
index 000000000..8bdece372
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api10Adapter.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.android.apps.mytracks.util;
+
+import com.google.android.apps.mytracks.Constants;
+import com.google.android.apps.mytracks.services.sensors.BluetoothConnectionManager;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * API level 10 specific implementation of the {@link ApiAdapter}.
+ *
+ * @author Jimmy Shih
+ */
+public class Api10Adapter extends Api9Adapter {
+
+ @Override
+ public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
+ try {
+ return bluetoothDevice.createInsecureRfcommSocketToServiceRecord(
+ BluetoothConnectionManager.SPP_UUID);
+ } catch (IOException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ }
+ return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.SPP_UUID);
+ };
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel3Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
similarity index 63%
rename from MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel3Adapter.java
rename to MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
index dcd033e14..6b572d045 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel3Adapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api7Adapter.java
@@ -15,50 +15,32 @@
*/
package com.google.android.apps.mytracks.util;
-import static com.google.android.apps.mytracks.Constants.TAG;
-
+import com.google.android.apps.mytracks.Constants;
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
+import com.google.android.apps.mytracks.services.sensors.BluetoothConnectionManager;
import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerTask;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
+import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
- * API level 3 specific implementation of the {@link ApiLevelAdapter}.
+ * API level 7 specific implementation of the {@link ApiAdapter}.
*
* @author Bartlomiej Niechwiej
*/
-public class ApiLevel3Adapter implements ApiLevelAdapter {
+public class Api7Adapter implements ApiAdapter {
- @Override
- public void startForeground(Service service,
- NotificationManager notificationManager, int id,
- Notification notification) {
- setServiceForeground(service, true);
-
- notificationManager.notify(id, notification);
- }
-
- @Override
- public void stopForeground(Service service,
- NotificationManager notificationManager, int id) {
- setServiceForeground(service, false);
- if (id != -1) {
- notificationManager.cancel(id);
- }
- }
-
@Override
public PeriodicTask getStatusAnnouncerTask(Context context) {
return new StatusAnnouncerTask(context);
@@ -74,24 +56,6 @@ public class ApiLevel3Adapter implements ApiLevelAdapter {
};
}
- private void setServiceForeground(Service service, boolean foreground) {
- // setForeground has been completely removed in API level 11, so we use reflection.
- try {
- Method setForegroundMethod = Service.class.getMethod("setForeground", boolean.class);
- setForegroundMethod.invoke(service, foreground);
- } catch (SecurityException e) {
- Log.e(TAG, "Unable to set service foreground state", e);
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Unable to set service foreground state", e);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Unable to set service foreground state", e);
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Unable to set service foreground state", e);
- } catch (InvocationTargetException e) {
- Log.e(TAG, "Unable to set service foreground state", e);
- }
- }
-
@Override
public void applyPreferenceChanges(Editor editor) {
editor.commit();
@@ -114,4 +78,25 @@ public class ApiLevel3Adapter implements ApiLevelAdapter {
public HttpTransport getHttpTransport() {
return new ApacheHttpTransport();
}
+
+ @Override
+ public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException {
+ try {
+ Class extends BluetoothDevice> c = bluetoothDevice.getClass();
+ Method insecure = c.getMethod("createInsecureRfcommSocket", Integer.class);
+ insecure.setAccessible(true);
+ return (BluetoothSocket) insecure.invoke(bluetoothDevice, 1);
+ } catch (SecurityException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ } catch (NoSuchMethodException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ } catch (IllegalArgumentException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ } catch (IllegalAccessException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ } catch (InvocationTargetException e) {
+ Log.d(Constants.TAG, "Unable to create insecure connection", e);
+ }
+ return bluetoothDevice.createRfcommSocketToServiceRecord(BluetoothConnectionManager.SPP_UUID);
+ }
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel8Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api8Adapter.java
similarity index 50%
rename from MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel8Adapter.java
rename to MyTracks/src/com/google/android/apps/mytracks/util/Api8Adapter.java
index 35696189d..f8371e081 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel8Adapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api8Adapter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package com.google.android.apps.mytracks.util;
import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
@@ -8,11 +23,11 @@ import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
import android.content.Context;
/**
- * API level 8 specific implementation of the {@link ApiLevelAdapter}.
+ * API level 8 specific implementation of the {@link ApiAdapter}.
*
* @author Jimmy Shih
*/
-public class ApiLevel8Adapter extends ApiLevel5Adapter {
+public class Api8Adapter extends Api7Adapter {
@Override
public PeriodicTask getStatusAnnouncerTask(Context context) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel9Adapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/Api9Adapter.java
similarity index 61%
rename from MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel9Adapter.java
rename to MyTracks/src/com/google/android/apps/mytracks/util/Api9Adapter.java
index 5ab6ab0aa..86fb12d3d 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel9Adapter.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/Api9Adapter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
package com.google.android.apps.mytracks.util;
import com.google.android.apps.mytracks.Constants;
@@ -11,11 +26,11 @@ import android.util.Log;
import java.util.Arrays;
/**
- * API level 9 specific implementation of the {@link ApiLevelAdapter}.
+ * API level 9 specific implementation of the {@link ApiAdapter}.
*
* @author Rodrigo Damazio
*/
-public class ApiLevel9Adapter extends ApiLevel8Adapter {
+public class Api9Adapter extends Api8Adapter {
@Override
public void applyPreferenceChanges(Editor editor) {
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java
new file mode 100644
index 000000000..868c43af1
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapter.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.android.apps.mytracks.util;
+
+import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
+import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
+import com.google.api.client.http.HttpTransport;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.io.IOException;
+
+/**
+ * A set of methods that may be implemented differently depending on the Android
+ * API level.
+ *
+ * @author Bartlomiej Niechwiej
+ */
+public interface ApiAdapter {
+
+ /**
+ * Gets a status announcer task.
+ *
+ * Due to changes in API level 8.
+ *
+ * @param context the context
+ */
+ public PeriodicTask getStatusAnnouncerTask(Context context);
+
+ /**
+ * Gets a {@link BackupPreferencesListener}.
+ *
+ * Due to changes in API level 8.
+ *
+ * @param context the context
+ */
+ public BackupPreferencesListener getBackupPreferencesListener(Context context);
+
+ /**
+ * Applies all the changes done to a given preferences editor. Changes may or
+ * may not be applied immediately.
+ *
+ * Due to changes in API level 9.
+ *
+ * @param editor the editor
+ */
+ public void applyPreferenceChanges(SharedPreferences.Editor editor);
+
+ /**
+ * Enables strict mode where supported, only if this is a development build.
+ *
+ * Due to changes in API level 9.
+ */
+ public void enableStrictMode();
+
+ /**
+ * Copies elements from an input byte array into a new byte array, from
+ * indexes start (inclusive) to end (exclusive). The end index must be less
+ * than or equal to the input length.
+ *
+ * Due to changes in API level 9.
+ *
+ * @param input the input byte array
+ * @param start the start index
+ * @param end the end index
+ * @return a new array containing elements from the input byte array.
+ */
+ public byte[] copyByteArray(byte[] input, int start, int end);
+
+ /**
+ * Gets a {@link HttpTransport}.
+ *
+ * Due to changes in API level 9.
+ */
+ public HttpTransport getHttpTransport();
+
+ /**
+ * Gets a {@link BluetoothSocket}.
+ *
+ * Due to changes in API level 10.
+ *
+ * @param bluetoothDevice
+ */
+ public BluetoothSocket getBluetoothSocket(BluetoothDevice bluetoothDevice) throws IOException;
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java
new file mode 100644
index 000000000..5af16534e
--- /dev/null
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiAdapterFactory.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.android.apps.mytracks.util;
+
+import android.os.Build;
+
+/**
+ * A factory to get the {@link ApiAdapter} for the current device.
+ *
+ * @author Rodrigo Damazio
+ */
+public class ApiAdapterFactory {
+
+ private static ApiAdapter apiAdapter;
+
+ /**
+ * Gets the {@link ApiAdapter} for the current device.
+ */
+ public static ApiAdapter getApiAdapter() {
+ if (apiAdapter == null) {
+ if (Build.VERSION.SDK_INT >= 10) {
+ apiAdapter = new Api10Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 9) {
+ apiAdapter = new Api9Adapter();
+ return apiAdapter;
+ } else if (Build.VERSION.SDK_INT >= 8) {
+ apiAdapter = new Api8Adapter();
+ return apiAdapter;
+ } else {
+ apiAdapter = new Api7Adapter();
+ return apiAdapter;
+ }
+ }
+ return apiAdapter;
+ }
+}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java
deleted file mode 100644
index 700b5a2a6..000000000
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.android.apps.mytracks.util;
-
-import com.google.android.apps.mytracks.Constants;
-
-import android.os.Build;
-import android.util.Log;
-
-/**
- * Utility class for determining if newer-API features are available on the
- * current device.
- *
- * @author Rodrigo Damazio
- */
-public class ApiFeatures {
-
- /**
- * The API level of the Android version we are being run under.
- */
- private static final int ANDROID_API_LEVEL = Integer.parseInt(
- Build.VERSION.SDK);
-
- private static ApiFeatures instance;
-
- /**
- * The API level adapter for the Android version we are being run under.
- */
- private ApiLevelAdapter apiLevelAdapter;
-
- /**
- * Returns the singleton instance of this class.
- */
- public static ApiFeatures getInstance() {
- if (instance == null) {
- instance = new ApiFeatures();
- }
- return instance;
- }
-
- /**
- * Injects a specific singleton instance, to be used for unit tests.
- */
- @SuppressWarnings("hiding")
- public static void injectInstance(ApiFeatures instance) {
- ApiFeatures.instance = instance;
- }
-
- /**
- * Allow subclasses for mocking, but no direct instantiation.
- */
- protected ApiFeatures() {
- // It is safe to import unsupported classes as long as we only actually
- // load the class when supported.
- if (getApiLevel() >= 9) {
- apiLevelAdapter = new ApiLevel9Adapter();
- } else if (getApiLevel() >= 8) {
- apiLevelAdapter = new ApiLevel8Adapter();
- } else if (getApiLevel() >= 5) {
- apiLevelAdapter = new ApiLevel5Adapter();
- } else {
- apiLevelAdapter = new ApiLevel3Adapter();
- }
-
- Log.i(Constants.TAG, "Using API level adapter " + apiLevelAdapter.getClass());
- }
-
- public ApiLevelAdapter getApiAdapter() {
- return apiLevelAdapter;
- }
-
- // API Level 4 Changes
-
- /**
- * Returns whether text-to-speech is available.
- */
- public boolean hasTextToSpeech() {
- return getApiLevel() >= 4;
- }
-
- // API Level 5 Changes
-
- /**
- * There's a bug (#1587) in Cupcake and Donut which prevents you from
- * using a SQLiteQueryBuilder twice. That is, if you call buildQuery
- * on a given instance (to log the statement for debugging), and then
- * call query on the same instance to make it actually do the query,
- * it'll regenerate the query for the second call, and will screw it
- * up. Specifically, it'll add extra parens which don't belong.
- */
- public boolean canReuseSQLiteQueryBuilder() {
- return getApiLevel() >= 5;
- }
-
- // API Level 10 changes
-
- /**
- * Returns true if BluetoothDevice.createInsecureRfcommSocketToServiceRecord
- * is available.
- */
- public boolean hasBluetoothDeviceCreateInsecureRfcommSocketToServiceRecord() {
- return getApiLevel() >= 10;
- }
-
- // Visible for testing.
- protected int getApiLevel() {
- return ANDROID_API_LEVEL;
- }
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevelAdapter.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevelAdapter.java
deleted file mode 100644
index f2180dfc2..000000000
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevelAdapter.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.android.apps.mytracks.util;
-
-import com.google.android.apps.mytracks.io.backup.BackupPreferencesListener;
-import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
-import com.google.api.client.http.HttpTransport;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
-import android.content.Context;
-import android.content.SharedPreferences;
-
-/**
- * A set of methods that may be implemented differently depending on the Android API level.
- *
- * @author Bartlomiej Niechwiej
- */
-public interface ApiLevelAdapter {
-
- /**
- * Puts the specified service into foreground.
- *
- * Due to changes in API level 5.
- *
- * @param service the service to be put in foreground.
- * @param notificationManager the notification manager used to post the given
- * notification.
- * @param id the ID of the notification, unique within the application.
- * @param notification the notification to post.
- */
- void startForeground(Service service, NotificationManager notificationManager,
- int id, Notification notification);
-
- /**
- * Puts the given service into background.
- *
- * Due to changes in API level 5.
- *
- * @param service the service to put into background.
- * @param notificationManager the notification manager to user when removing
- * notifications.
- * @param id the ID of the notification to be remove, or -1 if the
- * notification shouldn't be removed.
- */
- void stopForeground(Service service, NotificationManager notificationManager,
- int id);
-
- /**
- * Gets a status announcer task.
- *
- * Due to changes in API level 8.
- */
- PeriodicTask getStatusAnnouncerTask(Context context);
-
- /**
- * Gets a {@link BackupPreferencesListener}.
- *
- * Due to changes in API level 8.
- */
- BackupPreferencesListener getBackupPreferencesListener(Context context);
-
- /**
- * Applies all changes done to the given preferences editor.
- * Changes may or may not be applied immediately.
- *
- * Due to changes in API level 9.
- */
- void applyPreferenceChanges(SharedPreferences.Editor editor);
-
- /**
- * Enables strict mode where supported, only if this is a development build.
- *
- * Due to changes in API level 9.
- */
- void enableStrictMode();
-
- /**
- * Copies elements from the input byte array into a new byte array, from
- * indexes start (inclusive) to end (exclusive). The end index must be less
- * than or equal to input.length.
- *
- * Due to changes in API level 9.
- *
- * @param input the input byte array
- * @param start the start index
- * @param end the end index
- * @return a new array containing elements from the input byte array
- */
- byte[] copyByteArray(byte[] input, int start, int end);
-
-
- /**
- * Gets a {@link HttpTransport}.
- *
- * Due to changes in API level 9.
- */
- HttpTransport getHttpTransport();
-}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/BluetoothDeviceUtils.java b/MyTracks/src/com/google/android/apps/mytracks/util/BluetoothDeviceUtils.java
index 96e8bd878..548d873c7 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/BluetoothDeviceUtils.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/BluetoothDeviceUtils.java
@@ -15,186 +15,49 @@
*/
package com.google.android.apps.mytracks.util;
-import com.google.android.apps.mytracks.Constants;
-
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.os.Build;
-import android.util.Log;
import java.util.List;
import java.util.Set;
/**
* Utilities for dealing with bluetooth devices.
- * This can be used safely even in systems that don't support bluetooth,
- * in which case a dummy implementation will be used.
*
* @author Rodrigo Damazio
*/
-public abstract class BluetoothDeviceUtils {
- public static final String ANY_DEVICE = "any";
- private static BluetoothDeviceUtils instance;
+public class BluetoothDeviceUtils {
+ private BluetoothDeviceUtils() {}
+
/**
- * Dummy implementation, for systems that don't support bluetooth.
- */
- private static class DummyImpl extends BluetoothDeviceUtils {
- @Override
- public void populateDeviceLists(List deviceNames, List deviceAddresses) {
- // Do nothing - no devices to add
- }
-
- @Override
- public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
- return null;
- }
- }
-
- /**
- * Real implementation, for systems that DO support bluetooth.
- */
- private static class RealImpl extends BluetoothDeviceUtils {
- private final BluetoothAdapter bluetoothAdapter;
-
- public RealImpl() {
- bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
-
- if (bluetoothAdapter == null) {
- throw new IllegalStateException("Unable to get bluetooth adapter");
- }
- }
-
- @Override
- public void populateDeviceLists(List deviceNames, List deviceAddresses) {
- ensureNotDiscovering();
-
- Set pairedDevices = bluetoothAdapter.getBondedDevices();
- for (BluetoothDevice device : pairedDevices) {
- BluetoothClass bluetoothClass = device.getBluetoothClass();
- if (bluetoothClass != null) {
- // Not really sure what we want, but I know what we don't want.
- switch(bluetoothClass.getMajorDeviceClass()) {
- case BluetoothClass.Device.Major.COMPUTER:
- case BluetoothClass.Device.Major.PHONE:
- break;
- default:
- deviceAddresses.add(device.getAddress());
- deviceNames.add(device.getName());
- }
- }
- }
- }
-
- @Override
- public BluetoothDevice findDeviceMatching(String targetDeviceAddress) {
- if (targetDeviceAddress.equals(ANY_DEVICE)) {
- return findAnyDevice();
- } else {
- return findDeviceByAddress(targetDeviceAddress);
- }
- }
-
- /**
- * Finds and returns the first suitable bluetooth sensor.
- */
- private BluetoothDevice findAnyDevice() {
- ensureNotDiscovering();
-
- Set pairedDevices = bluetoothAdapter.getBondedDevices();
- for (BluetoothDevice device : pairedDevices) {
- // Look for the first paired computer device
- if (isSuitableDevice(device)) {
- return device;
- }
- }
-
- return null;
- }
-
- /**
- * Finds and returns a device with the given address, or null if it's not
- * a suitable sensor.
- */
- private BluetoothDevice findDeviceByAddress(String targetDeviceAddress) {
- ensureNotDiscovering();
-
- BluetoothDevice device = bluetoothAdapter.getRemoteDevice(targetDeviceAddress);
- if (isSuitableDevice(device)) {
- return device;
- }
-
- return null;
- }
-
- /**
- * Ensures the bluetooth adapter is not in discovery mode.
- */
- private void ensureNotDiscovering() {
- // If it's in discovery mode, cancel that for now.
- bluetoothAdapter.cancelDiscovery();
- }
-
- /**
- * Checks whether the given device is a suitable sensor.
- *
- * @param device the device to check
- * @return true if it's suitable, false otherwise
- */
- private boolean isSuitableDevice(BluetoothDevice device) {
- // Check that the device is bonded
- if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
- return false;
- }
-
- return true;
- }
- }
-
- /**
- * Returns the proper (singleton) instance of this class.
- */
- public static BluetoothDeviceUtils getInstance() {
- if (instance == null) {
- if (!isBluetoothMethodSupported()) {
- Log.d(Constants.TAG, "Using dummy bluetooth utils");
- instance = new DummyImpl();
- } else {
- Log.d(Constants.TAG, "Using real bluetooth utils");
- try {
- instance = new RealImpl();
- } catch (IllegalStateException ise) {
- Log.w(Constants.TAG, "Oops, I mean, using dummy bluetooth utils", ise);
- instance = new DummyImpl();
- }
- }
- }
- return instance;
- }
-
- /**
- * Populates the given lists with the names and addresses of all suitable
+ * Populates the device names and the device addresses with all the suitable
* bluetooth devices.
*
- * @param deviceNames the list to populate with user-visible names
- * @param deviceAddresses the list to populate with device addresses
+ * @param bluetoothAdapter the bluetooth adapter
+ * @param deviceNames list of device names
+ * @param deviceAddresses list of device addresses
*/
- public abstract void populateDeviceLists(List deviceNames, List deviceAddresses);
+ public static void populateDeviceLists(
+ BluetoothAdapter bluetoothAdapter, List deviceNames, List deviceAddresses) {
+ // Ensure the bluetooth adapter is not in discovery mode.
+ bluetoothAdapter.cancelDiscovery();
- /**
- * Finds the bluetooth device with the given address.
- *
- * @param targetDeviceAddress the address of the device, or
- * {@link #ANY_DEVICE} for using the first suitable device
- * @return the device's descriptor, or null if not found
- */
- public abstract BluetoothDevice findDeviceMatching(String targetDeviceAddress);
-
- /**
- * @return whether the bluetooth method is supported on this device
- */
- public static boolean isBluetoothMethodSupported() {
- return Integer.parseInt(Build.VERSION.SDK) >= 5;
+ Set pairedDevices = bluetoothAdapter.getBondedDevices();
+ for (BluetoothDevice device : pairedDevices) {
+ BluetoothClass bluetoothClass = device.getBluetoothClass();
+ if (bluetoothClass != null) {
+ // Not really sure what we want, but I know what we don't want.
+ switch (bluetoothClass.getMajorDeviceClass()) {
+ case BluetoothClass.Device.Major.COMPUTER:
+ case BluetoothClass.Device.Major.PHONE:
+ break;
+ default:
+ deviceAddresses.add(device.getAddress());
+ deviceNames.add(device.getName());
+ }
+ }
+ }
}
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtil.java b/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtil.java
index 0008b6e8e..6d2253844 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtil.java
+++ b/MyTracks/src/com/google/android/apps/mytracks/util/EulaUtil.java
@@ -45,7 +45,7 @@ public class EulaUtil {
EULA_PREFERENCE_FILE, Context.MODE_PRIVATE);
Editor editor = preferences.edit();
editor.putBoolean(EULA_PREFERENCE_KEY, true);
- ApiFeatures.getInstance().getApiAdapter().applyPreferenceChanges(editor);
+ ApiAdapterFactory.getApiAdapter().applyPreferenceChanges(editor);
}
public static String getEulaMessage(Context context) {
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/TestRecordingService.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/TestRecordingService.java
new file mode 100644
index 000000000..2eaa20aad
--- /dev/null
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/TestRecordingService.java
@@ -0,0 +1,64 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.google.android.apps.mytracks.services;
+
+import android.app.Notification;
+import android.app.Service;
+import android.test.ServiceTestCase;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * A {@link TrackRecordingService} that can be used with {@link ServiceTestCase}.
+ * {@link ServiceTestCase} throws a null pointer exception when the service
+ * calls {@link Service#startForeground(int, android.app.Notification)} and
+ * {@link Service#stopForeground(boolean)}.
+ *
+ * See http://code.google.com/p/android/issues/detail?id=12122
+ *
+ * Wrap these two methods in wrappers and override them.
+ *
+ * @author Jimmy Shih
+ */
+public class TestRecordingService extends TrackRecordingService {
+
+ private static final String TAG = TestRecordingService.class.getSimpleName();
+
+ @Override
+ protected void startForegroundService(Notification notification) {
+ try {
+ Method setForegroundMethod = Service.class.getMethod("setForeground", boolean.class);
+ setForegroundMethod.invoke(this, true);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ }
+ }
+
+ @Override
+ protected void stopForegroundService() {
+ try {
+ Method setForegroundMethod = Service.class.getMethod("setForeground", boolean.class);
+ setForegroundMethod.invoke(this, false);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "Unable to start a service in foreground", e);
+ }
+ }
+}
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
index 25fe512ef..df0b079a3 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/TrackRecordingServiceTest.java
@@ -24,7 +24,6 @@ import com.google.android.apps.mytracks.content.Track;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.content.WaypointCreationRequest;
import com.google.android.apps.mytracks.stats.TripStatistics;
-import com.google.android.apps.mytracks.util.ApiFeatures;
import com.google.android.maps.mytracks.R;
import android.content.BroadcastReceiver;
@@ -57,8 +56,7 @@ import java.util.List;
* is called, which prevents from testing multiple scenarios in a single
* test (see runFunctionTest for more details).
*/
-public class TrackRecordingServiceTest
- extends ServiceTestCase {
+public class TrackRecordingServiceTest extends ServiceTestCase {
private Context context;
private MyTracksProviderUtils providerUtils;
@@ -74,7 +72,7 @@ public class TrackRecordingServiceTest
private Intent serviceIntent;
public TrackRecordingServiceTest() {
- super(TrackRecordingService.class);
+ super(TestRecordingService.class);
}
/**
@@ -96,19 +94,6 @@ public class TrackRecordingServiceTest
}
}
- /**
- * A mock class that forces API level < 5 to make sure we can workaround a bug
- * in ServiceTestCase (throwing a NPE).
- * See http://code.google.com/p/android/issues/detail?id=12122 for more
- * details.
- */
- private static class MockApiFeatures extends ApiFeatures {
- @Override
- protected int getApiLevel() {
- return 4;
- }
- }
-
@Override
protected IBinder bindService(Intent intent) {
if (getService() != null) {
@@ -138,8 +123,6 @@ public class TrackRecordingServiceTest
protected void setUp() throws Exception {
super.setUp();
- ApiFeatures.injectInstance(new MockApiFeatures());
-
MockContentResolver mockContentResolver = new MockContentResolver();
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
getContext(), getContext(), "test.");
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactoryTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactoryTest.java
index ea9a8d441..91d9b3eeb 100644
--- a/MyTracksTest/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactoryTest.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/services/tasks/StatusAnnouncerFactoryTest.java
@@ -15,13 +15,6 @@
*/
package com.google.android.apps.mytracks.services.tasks;
-import com.google.android.apps.mytracks.services.tasks.PeriodicTask;
-import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerFactory;
-import com.google.android.apps.mytracks.services.tasks.StatusAnnouncerTask;
-import com.google.android.apps.mytracks.util.ApiFeatures;
-
-import android.media.AudioManager;
-import android.speech.tts.TextToSpeech;
import android.test.AndroidTestCase;
/**
@@ -31,62 +24,9 @@ import android.test.AndroidTestCase;
* @author Rodrigo Damazio
*/
public class StatusAnnouncerFactoryTest extends AndroidTestCase {
- /**
- * Mock version of the {@link ApiFeatures} class.
- */
- private class MockApiFeatures extends ApiFeatures {
- private boolean hasTts;
-
- public void setHasTextToSpeech(boolean hasTts) {
- this.hasTts = hasTts;
- }
-
- @Override
- public boolean hasTextToSpeech() {
- return hasTts;
- }
- }
-
- private MockApiFeatures apiFeatures;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- apiFeatures = new MockApiFeatures();
- }
-
public void testCreate() {
- apiFeatures.setHasTextToSpeech(true);
- PeriodicTaskFactory factory =
- new StatusAnnouncerFactory(apiFeatures);
+ PeriodicTaskFactory factory = new StatusAnnouncerFactory();
PeriodicTask task = factory.create(getContext());
assertTrue(task instanceof StatusAnnouncerTask);
}
-
- public void testCreate_notAvailable() {
- apiFeatures.setHasTextToSpeech(false);
- PeriodicTaskFactory factory =
- new StatusAnnouncerFactory(apiFeatures);
- PeriodicTask task = factory.create(getContext());
- assertNull(task);
- }
-
- public void testGetVolumeStream() {
- apiFeatures.setHasTextToSpeech(true);
- StatusAnnouncerFactory factory =
- new StatusAnnouncerFactory(apiFeatures);
- assertEquals(
- TextToSpeech.Engine.DEFAULT_STREAM,
- factory.getVolumeStream());
- }
-
- public void testGetVolumeStream_notAvailable() {
- apiFeatures.setHasTextToSpeech(false);
- StatusAnnouncerFactory factory =
- new StatusAnnouncerFactory(apiFeatures);
- assertEquals(
- AudioManager.USE_DEFAULT_STREAM_TYPE,
- factory.getVolumeStream());
- }
}
diff --git a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel5Adapter.java b/MyTracksTest/src/com/google/android/apps/mytracks/util/ApiAdapterFactoryTest.java
similarity index 51%
rename from MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel5Adapter.java
rename to MyTracksTest/src/com/google/android/apps/mytracks/util/ApiAdapterFactoryTest.java
index 0f963f70f..4b73a6a6f 100644
--- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiLevel5Adapter.java
+++ b/MyTracksTest/src/com/google/android/apps/mytracks/util/ApiAdapterFactoryTest.java
@@ -15,27 +15,15 @@
*/
package com.google.android.apps.mytracks.util;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.Service;
+import junit.framework.TestCase;
/**
- * API level 5 specific implementation of the {@link ApiLevelAdapter}.
+ * Tests {@link ApiAdapterFactory}.
*
- * @author Bartlomiej Niechwiej
+ * @author Rodrigo Damazio
*/
-public class ApiLevel5Adapter extends ApiLevel3Adapter {
-
- @Override
- public void startForeground(Service service,
- NotificationManager notificationManager, int id,
- Notification notification) {
- service.startForeground(id, notification);
- }
-
- @Override
- public void stopForeground(Service service,
- NotificationManager notificationManager, int id) {
- service.stopForeground(id != -1);
+public class ApiAdapterFactoryTest extends TestCase {
+ public void testGetApiLevelAdapter() {
+ assertNotNull(ApiAdapterFactory.getApiAdapter());
}
}
diff --git a/MyTracksTest/src/com/google/android/apps/mytracks/util/ApiFeaturesTest.java b/MyTracksTest/src/com/google/android/apps/mytracks/util/ApiFeaturesTest.java
deleted file mode 100644
index 5b08a975f..000000000
--- a/MyTracksTest/src/com/google/android/apps/mytracks/util/ApiFeaturesTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.android.apps.mytracks.util;
-
-import junit.framework.TestCase;
-
-/**
- * Tests the API feature detection code in {@link ApiFeatures}.
- * This test requires Froyo+ to run.
- *
- * @author Rodrigo Damazio
- */
-public class ApiFeaturesTest extends TestCase {
-
- private TestableApiFeatures features;
-
- private class TestableApiFeatures extends ApiFeatures {
- private int apiLevel;
-
- public void setApiLevel(int apiLevel) {
- this.apiLevel = apiLevel;
- }
-
- @Override
- protected int getApiLevel() {
- return apiLevel;
- }
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- features = new TestableApiFeatures();
- }
-
- public void testHasTextToSpeech() {
- features.setApiLevel(3);
- assertFalse(features.hasTextToSpeech());
- for (int i = 4; i <= 8; i++) {
- features.setApiLevel(i);
- assertTrue(features.hasTextToSpeech());
- }
- }
-
- public void testGetApiAdapter() {
- assertNotNull(features.getApiAdapter());
- }
-}