mirror of
https://codeberg.org/OpenTracksApp/OpenTracks.git
synced 2026-10-03 01:53:05 +02:00
Introduce an interface to decouple platform specific API from the classes that require it. Also:
1) Put the service into foreground to prevent from being killed by the system 2) Fix a bug the prevented MyTracks from running on pre-Eclair devices
This commit is contained in:
@@ -379,7 +379,8 @@ public class MyTracksMap extends MapActivity
|
||||
protected void onDestroy() {
|
||||
Log.d(MyTracksConstants.TAG, "MyTracksMap.onDestroy");
|
||||
if (updateTrackThread != null) {
|
||||
updateTrackThread.quit();
|
||||
ApiFeatures.getInstance().getApiPlatformAdapter().stopHandlerThread(
|
||||
updateTrackThread);
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.google.android.apps.mytracks.content.WaypointsColumns;
|
||||
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.ApiPlatformAdapter;
|
||||
import com.google.android.apps.mytracks.util.MyTracksUtils;
|
||||
import com.google.android.apps.mytracks.util.StringUtils;
|
||||
import com.google.android.maps.mytracks.R;
|
||||
@@ -329,6 +330,8 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
* Shows the notification message and icon in the notification bar.
|
||||
*/
|
||||
public void showNotification() {
|
||||
final ApiPlatformAdapter apiPlatformAdapter =
|
||||
ApiFeatures.getInstance().getApiPlatformAdapter();
|
||||
if (isRecording) {
|
||||
Notification notification = new Notification(
|
||||
R.drawable.arrow_320, null /* tickerText */,
|
||||
@@ -339,9 +342,10 @@ public class TrackRecordingService extends Service implements LocationListener {
|
||||
notification.setLatestEventInfo(this, getString(R.string.app_name),
|
||||
getString(R.string.recording_your_track), contentIntent);
|
||||
notification.flags += Notification.FLAG_NO_CLEAR;
|
||||
notificationManager.notify(1, notification);
|
||||
apiPlatformAdapter.startForeground(this, notificationManager, 1,
|
||||
notification);
|
||||
} else {
|
||||
notificationManager.cancelAll();
|
||||
apiPlatformAdapter.stopForeground(this, notificationManager, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package com.google.android.apps.mytracks.util;
|
||||
|
||||
import com.google.android.apps.mytracks.MyTracksConstants;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utility class for determining if newer-API features are available on the
|
||||
@@ -28,9 +31,15 @@ public class ApiFeatures {
|
||||
/**
|
||||
* The API level of the Android version we're being run under.
|
||||
*/
|
||||
public static final int ANDROID_API_LEVEL = Integer.parseInt(Build.VERSION.SDK);
|
||||
|
||||
public static final int ANDROID_API_LEVEL = Integer.parseInt(
|
||||
Build.VERSION.SDK);
|
||||
|
||||
private static ApiFeatures instance;
|
||||
|
||||
/**
|
||||
* The API platform adapter supported by this system.
|
||||
*/
|
||||
private ApiPlatformAdapter apiPlatformAdapter;
|
||||
|
||||
/**
|
||||
* Returns the singleton instance of this class.
|
||||
@@ -52,7 +61,28 @@ public class ApiFeatures {
|
||||
/**
|
||||
* Allow subclasses for mocking, but no direct instantiation.
|
||||
*/
|
||||
protected ApiFeatures() {}
|
||||
protected ApiFeatures() {
|
||||
if (getApiLevel() >= 5) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(
|
||||
"com.google.android.apps.mytracks.util.EclairPlatformAdapter");
|
||||
apiPlatformAdapter = (ApiPlatformAdapter) clazz.newInstance();
|
||||
} catch (Exception e) {
|
||||
Log.i(MyTracksConstants.TAG, "ApiFeatures: Unable to instantiate Eclair"
|
||||
+ " platform adapter", e);
|
||||
}
|
||||
}
|
||||
if (apiPlatformAdapter == null) {
|
||||
Log.i(MyTracksConstants.TAG,
|
||||
"ApiFeatures: Using default platform adapter");
|
||||
// Cupcake adapter is always supported, so it's safe to do static linkage.
|
||||
apiPlatformAdapter = new CupcakePlatformAdapter();
|
||||
}
|
||||
}
|
||||
|
||||
public ApiPlatformAdapter getApiPlatformAdapter() {
|
||||
return apiPlatformAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether cloud backup (a.k.a. Froyo backup) is available.
|
||||
@@ -81,7 +111,7 @@ public class ApiFeatures {
|
||||
public boolean hasModernSignalStrength() {
|
||||
return getApiLevel() >= 7;
|
||||
}
|
||||
|
||||
|
||||
// Visible for testing.
|
||||
protected int getApiLevel() {
|
||||
return ANDROID_API_LEVEL;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
/**
|
||||
* A set of methods that may be implemented in a platform specific way.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public interface ApiPlatformAdapter {
|
||||
|
||||
/**
|
||||
* Puts the specified service into foreground.
|
||||
*
|
||||
* @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.
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* Terminates the given handler thread.
|
||||
*
|
||||
* @param handlerThread the thread to be terminated.
|
||||
* @return true whether the handler has been stopped or not.
|
||||
*/
|
||||
boolean stopHandlerThread(HandlerThread handlerThread);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
/**
|
||||
* The Cupcake (API level 3) specific implementation of the
|
||||
* {@link ApiPlatformAdapter}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class CupcakePlatformAdapter implements ApiPlatformAdapter {
|
||||
|
||||
@Override
|
||||
public void startForeground(Service service,
|
||||
NotificationManager notificationManager, int id,
|
||||
Notification notification) {
|
||||
service.setForeground(true);
|
||||
notificationManager.notify(id, notification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopForeground(Service service,
|
||||
NotificationManager notificationManager, int id) {
|
||||
service.setForeground(false);
|
||||
if (id != -1) {
|
||||
notificationManager.cancel(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopHandlerThread(HandlerThread handlerThread) {
|
||||
// Do nothing, as Cupcake doesn't provide quit().
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.Service;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
/**
|
||||
* The Eclair (API level 5) specific implementation of the
|
||||
* {@link ApiPlatformAdapter}.
|
||||
*
|
||||
* @author Bartlomiej Niechwiej
|
||||
*/
|
||||
public class EclairPlatformAdapter implements ApiPlatformAdapter {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopHandlerThread(HandlerThread handlerThread) {
|
||||
return handlerThread.quit();
|
||||
}
|
||||
}
|
||||
+19
@@ -23,6 +23,7 @@ import com.google.android.apps.mytracks.content.MyTracksProviderUtils;
|
||||
import com.google.android.apps.mytracks.content.Track;
|
||||
import com.google.android.apps.mytracks.content.Waypoint;
|
||||
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.ContentResolver;
|
||||
@@ -57,6 +58,9 @@ public class TrackRecordingServiceTest
|
||||
super(TrackRecordingService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* A context wrapper with the user provided {@link ContentResolver}.
|
||||
*/
|
||||
private static class MockContext extends ContextWrapper {
|
||||
private final ContentResolver contentResolver;
|
||||
|
||||
@@ -70,11 +74,26 @@ public class TrackRecordingServiceTest
|
||||
return contentResolver;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
ApiFeatures.injectInstance(new MockApiFeatures());
|
||||
|
||||
MockContentResolver mockContentResolver = new MockContentResolver();
|
||||
RenamingDelegatingContext targetContext = new RenamingDelegatingContext(
|
||||
getContext(), getContext(), "test.");
|
||||
|
||||
@@ -75,4 +75,8 @@ public class ApiFeaturesTest extends TestCase {
|
||||
features.setApiLevel(8);
|
||||
assertTrue(features.hasModernSignalStrength());
|
||||
}
|
||||
|
||||
public void testGetApiPlatformAdapter() {
|
||||
assertNotNull(features.getApiPlatformAdapter());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user