From 5572e07b341036de2260715b4eb3deae3e04d9ad Mon Sep 17 00:00:00 2001 From: Jimmy Shih Date: Tue, 11 Oct 2011 14:54:49 -0700 Subject: [PATCH] - Use BluetoothDevice createInsecureRfcommSocketToServiceRecord for API level 10 or above. - Use DecimalFormatSymbols getInstance for API level 9 or above. - Use Arrays copyOfRange for API level 9 or above. - Remove ApiFeatures hasStrictMode. - Fix comment on MapOverlay to indicate that the two methods should only be used during testing. - Use android.text.format.DateFormat to format date and time in DocsHelper. This DateFormat uses the same locale format as the phone configuration. Fix DecimalFormat --- .../android/apps/mytracks/MapOverlay.java | 4 +- .../apps/mytracks/io/docs/DocsHelper.java | 11 ++- .../apps/mytracks/io/docs/DocsTagBuilder.java | 6 +- .../sensors/BluetoothConnectionManager.java | 30 ++++++- .../services/sensors/ZephyrMessageParser.java | 18 +++- .../apps/mytracks/util/ApiFeatures.java | 86 ++++++++++++------- 6 files changed, 110 insertions(+), 45 deletions(-) diff --git a/MyTracks/src/com/google/android/apps/mytracks/MapOverlay.java b/MyTracks/src/com/google/android/apps/mytracks/MapOverlay.java index a5e29a805..b4bb30db7 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/MapOverlay.java +++ b/MyTracks/src/com/google/android/apps/mytracks/MapOverlay.java @@ -305,12 +305,12 @@ public class MapOverlay extends Overlay implements OnSharedPreferenceChangeListe return new Rect(cx - w / 2, cy - h / 2, cx + w / 2, cy + h / 2); } - // Visible for testing + // For use in testing only. public TrackPathPainter getTrackPathPainter() { return trackPathPainter; } - // Visible for testing + // For use in testing only. public void setTrackPathPainter(TrackPathPainter trackPathPainter) { this.trackPathPainter = trackPathPainter; } diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java index ca02b7aec..101fd9256 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsHelper.java @@ -34,6 +34,7 @@ import com.google.wireless.gdata.parser.ParseException; import com.google.wireless.gdata2.client.AuthenticationException; import android.content.Context; +import android.text.format.DateFormat; import android.util.Log; import java.io.BufferedReader; @@ -44,7 +45,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLConnection; -import java.text.DateFormat; import java.util.Date; import java.util.concurrent.atomic.AtomicReference; @@ -287,13 +287,16 @@ public class DocsHelper { String elevationUnit = context.getString(metricUnits ? R.string.meter : R.string.feet); - DateFormat trackDateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); - + java.text.DateFormat dateFormat = DateFormat.getDateFormat(context); + java.text.DateFormat timeFormat = DateFormat.getTimeFormat(context); + Date startTime = new Date(stats.getStartTime()); + String dateString = dateFormat.format(startTime) + " " + timeFormat.format(startTime); + // Prepare the Post-Text we are going to send. DocsTagBuilder tagBuilder = new DocsTagBuilder(metricUnits) .append("name", track.getName()) .append("description", track.getDescription()) - .append("date", trackDateFormat.format(new Date(stats.getStartTime()))) + .append("date", dateString) .append("totaltime", StringUtils.formatTimeAlwaysShowingHours( stats.getTotalTime())) .append("movingtime", StringUtils.formatTimeAlwaysShowingHours( diff --git a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsTagBuilder.java b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsTagBuilder.java index b20ae2eb6..1b752cc79 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsTagBuilder.java +++ b/MyTracks/src/com/google/android/apps/mytracks/io/docs/DocsTagBuilder.java @@ -15,6 +15,7 @@ */ package com.google.android.apps.mytracks.io.docs; +import com.google.android.apps.mytracks.util.ApiFeatures; import com.google.android.apps.mytracks.util.StringUtils; import com.google.android.apps.mytracks.util.UnitConversions; @@ -45,8 +46,11 @@ import java.util.Locale; * @author Matthew Simmons */ class DocsTagBuilder { + private static final DecimalFormatSymbols FORMAT_SYMBOLS = - DecimalFormatSymbols.getInstance(Locale.ENGLISH); + ApiFeatures.getInstance().hasDecimalFormatSymbolsGetInstance() + ? DecimalFormatSymbols.getInstance(Locale.ENGLISH) + : new DecimalFormatSymbols(Locale.ENGLISH); private static final NumberFormat LARGE_UNIT_FORMAT = new DecimalFormat("#,###,###.00", FORMAT_SYMBOLS); private static final NumberFormat SMALL_UNIT_FORMAT = 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 082e8e0d1..9e1f59fe9 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,6 +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 android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -30,6 +31,8 @@ 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; /** @@ -259,10 +262,29 @@ public class BluetoothConnectionManager { } private BluetoothSocket getSocket() throws IOException { - try { - return device.createInsecureRfcommSocketToServiceRecord(SPP_UUID); - } catch (IOException e) { - Log.e(Constants.TAG, "Unable to get insecure connect.", e); + 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 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); } 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 24d93c2fd..4ea554d48 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,6 +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 java.util.Arrays; @@ -23,7 +24,7 @@ import java.util.Arrays; * An implementation of a Sensor MessageParser for Zephyr. * * @author Sandor Dornbush - * @author Dominik Ršttsches + * @author Dominik R�ttsches */ public class ZephyrMessageParser implements MessageParser { @@ -56,13 +57,24 @@ public class ZephyrMessageParser implements MessageParser { return sds.build(); } + /** + * Copies a byte array. In case Arrays.copyOfRange is not available. + */ + private byte[] copyByteArray(byte[] input, int from, int to) { + int length = to - from; + byte[] output = new byte[length]; + System.arraycopy(input, from, output, 0, length); + return output; + } + private void setCadence(Sensor.SensorDataSet.Builder sds, byte[] buffer) { // Device Firmware ID, Firmware Version, Hardware ID, Hardware Version // 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 = Arrays.copyOfRange(buffer, 3, 11); - + byte[] hardwareFirmwareId = ApiFeatures.getInstance().hasArraysCopyOfRange() + ? Arrays.copyOfRange(buffer, 3, 11) + : 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/util/ApiFeatures.java b/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java index 821e05c80..362385d0f 100644 --- a/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java +++ b/MyTracks/src/com/google/android/apps/mytracks/util/ApiFeatures.java @@ -17,10 +17,16 @@ package com.google.android.apps.mytracks.util; import com.google.android.apps.mytracks.Constants; import com.google.api.client.http.HttpTransport; +import com.google.api.client.http.apache.ApacheHttpTransport; +import com.google.api.client.http.javanet.NetHttpTransport; +import android.bluetooth.BluetoothDevice; import android.os.Build; import android.util.Log; +import java.text.DecimalFormatSymbols; +import java.util.Arrays; + /** * Utility class for determining if newer-API features are available on the * current device. @@ -32,7 +38,7 @@ public class ApiFeatures { /** * The API level of the Android version we're being run under. */ - public static final int ANDROID_API_LEVEL = Integer.parseInt( + private static final int ANDROID_API_LEVEL = Integer.parseInt( Build.VERSION.SDK); private static ApiFeatures instance; @@ -81,38 +87,17 @@ public class ApiFeatures { return apiPlatformAdapter; } - /** - * Returns whether cloud backup (a.k.a. Froyo backup) is available. - */ - public boolean hasBackup() { - return getApiLevel() >= 8; - } - + // API Level 4 Changes + /** * Returns whether text-to-speech is available. */ public boolean hasTextToSpeech() { - if (getApiLevel() < 4) return false; - - try { - Class.forName("android.speech.tts.TextToSpeech"); - } catch (ClassNotFoundException ex) { - return false; - } catch (LinkageError er) { - return false; - } - - return true; - } - - public boolean hasStrictMode() { - return getApiLevel() >= 9; - } - - public boolean isAudioFocusSupported() { - return getApiLevel() >= 8; + 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 @@ -122,18 +107,57 @@ public class ApiFeatures { * up. Specifically, it'll add extra parens which don't belong. */ public boolean canReuseSQLiteQueryBuilder() { - return getApiLevel() > 4; + return getApiLevel() >= 5; + } + + // API Level 8 Changes + + public boolean isAudioFocusSupported() { + return getApiLevel() >= 8; } /** - * Returns true if com.google.api.client.javanet.NetHttpTransport should be - * used rather than com.google.api.client.apache.ApacheHttpTransport. See - * {@link HttpTransport} for more info. + * Returns whether cloud backup (a.k.a. Froyo backup) is available. + */ + public boolean hasBackup() { + return getApiLevel() >= 8; + } + + // API Level 9 Changes + + /** + * Returns true if {@link NetHttpTransport} should be used rather than + * {@link ApacheHttpTransport}. See {@link HttpTransport} for more info. */ public boolean useNetHttpTransport() { return getApiLevel() >= 9; } + + /** + * Returns true if {@link DecimalFormatSymbols#getInstance} is available. + */ + public boolean hasDecimalFormatSymbolsGetInstance() { + return getApiLevel() >= 9; + } + + /** + * Returns true if {@link Arrays#copyOfRange} is available. + */ + public boolean hasArraysCopyOfRange() { + return getApiLevel() >= 9; + } + // API Level 10 changes + + /** + * Returns true if + * {@link BluetoothDevice#createInsecureRfcommSocketToServiceRecord} is + * available. + */ + public boolean hasBluetoothDeviceCreateInsecureRfcommSocketToServiceRecord() { + return getApiLevel() >= 10; + } + // Visible for testing. protected int getApiLevel() { return ANDROID_API_LEVEL;