Moving signal strength sampling into a separate app.

This commit is contained in:
Rodrigo Damazio
2010-11-26 23:51:32 -08:00
parent 7abce12499
commit 5971d67a66
49 changed files with 1341 additions and 315 deletions
@@ -0,0 +1,2 @@
package com.google.android.apps.mytracks.content;
parcelable Waypoint;
@@ -0,0 +1,189 @@
/*
* Copyright 2009 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.content;
import android.location.Location;
import android.os.Parcel;
import android.os.Parcelable;
/**
* A way point. It has a location, meta data such as name, description,
* category, and icon, plus it can store track statistics for a "sub-track".
*
* TODO: hashCode and equals
*
* @author Leif Hendrik Wilden
* @author Rodrigo Damazio
*/
public final class Waypoint implements Parcelable {
/**
* Creator for a Waypoint object
*/
public static class Creator implements Parcelable.Creator<Waypoint> {
public Waypoint createFromParcel(Parcel source) {
ClassLoader classLoader = getClass().getClassLoader();
Waypoint waypoint = new Waypoint();
waypoint.id = source.readLong();
waypoint.name = source.readString();
waypoint.description = source.readString();
waypoint.category = source.readString();
waypoint.icon = source.readString();
waypoint.trackId = source.readLong();
waypoint.type = source.readInt();
waypoint.startId = source.readLong();
waypoint.stopId = source.readLong();
if (source.readByte() > 0) {
throw new UnsupportedOperationException(
"No support for reading waypoints with stats");
}
byte hasLocation = source.readByte();
if (hasLocation > 0) {
waypoint.location = source.readParcelable(classLoader);
}
return waypoint;
}
public Waypoint[] newArray(int size) {
return new Waypoint[size];
}
}
public static final Creator CREATOR = new Creator();
public static final int TYPE_WAYPOINT = 0;
public static final int TYPE_STATISTICS = 1;
private long id = -1;
private String name = "";
private String description = "";
private String category = "";
private String icon = "";
private long trackId = -1;
private int type = 0;
private Location location;
/** Start track point id */
private long startId = -1;
/** Stop track point id */
private long stopId = -1;
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(id);
dest.writeString(name);
dest.writeString(description);
dest.writeString(category);
dest.writeString(icon);
dest.writeLong(trackId);
dest.writeInt(type);
dest.writeLong(startId);
dest.writeLong(stopId);
dest.writeByte((byte) 0); // No stats
dest.writeByte(location == null ? (byte) 0 : (byte) 1);
if (location != null) {
dest.writeParcelable(location, 0);
}
}
// Getters and setters:
//---------------------
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Location getLocation() {
return location;
}
public void setTrackId(long trackId) {
this.trackId = trackId;
}
public int describeContents() {
return 0;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTrackId() {
return trackId;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public long getStartId() {
return startId;
}
public void setStartId(long startId) {
this.startId = startId;
}
public long getStopId() {
return stopId;
}
public void setStopId(long stopId) {
this.stopId = stopId;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public void setLocation(Location location) {
this.location = location;
}
}
@@ -0,0 +1,87 @@
/*
* Copyright 2008 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.services;
import com.google.android.apps.mytracks.content.Waypoint;
/**
* MyTracks service.
* This service is the process that actually records and manages tracks.
*/
interface ITrackRecordingService {
/**
* Starts recording a new track.
*
* @return the track ID of the new track
*/
long startNewTrack();
/**
* Checks and returns whether we're currently recording a track.
*/
boolean isRecording();
/**
* Returns the track ID of the track currently being recorded, or -1 if none
* is being recorded. This ID can then be used to read track data from the
* content source.
*/
long getRecordingTrackId();
/**
* Inserts a waypoint marker in the track being recorded.
*
* @param waypoint the waypoint to insert
* @return the unique ID of the inserted marker
*/
long insertWaypointMarker(in Waypoint waypoint);
/**
* Inserts a statistics marker in the track being recorded.
*
* @param location the location at which to insert the marker
* @return the unique ID of the inserted marker
*/
long insertStatisticsMarker(in Location location);
/**
* Inserts a location in the track being recorded.
*
* When recording, locations detected by the GPS are already automatically
* added to the track, so this should be used only for adding special points
* or for testing.
*
* @param loc the location to insert
*/
void recordLocation(in Location loc);
/**
* Stops recording the current track.
*/
void endCurrentTrack();
/**
* Returns whether at least one track has been or is being recorded (and was
* not deleted).
*/
boolean hasRecorded();
/**
* Deletes all the stored tracks.
*/
void deleteAllTracks();
}
@@ -0,0 +1,61 @@
/*
* 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.signalstrength;
import static com.google.android.apps.mytracks.signalstrength.SignalStrengthConstants.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
/**
* Broadcast listener which gets notified when we start or stop recording a track.
*
* @author Rodrigo Damazio
*/
public class RecordingStateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context ctx, Intent intent) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
boolean autoStart = preferences.getBoolean(
ctx.getString(R.string.settings_auto_start_key), false);
if (!autoStart) {
Log.d(TAG, "Not auto-starting signal sampling");
return;
}
String action = intent.getAction();
if (START_TRACK_ACTION.equals(action) || RESUME_TRACK_ACTION.equals(action)) {
startService(ctx);
} else if (STOP_TRACK_ACTION.equals(action) || PAUSE_TRACK_ACTION.equals(action)) {
stopService(ctx);
} else {
Log.e(TAG, "Unknown action received: " + action);
}
}
// @VisibleForTesting
protected void stopService(Context ctx) {
SignalStrengthService.stopService(ctx);
}
// @VisibleForTesting
protected void startService(Context ctx) {
SignalStrengthService.startService(ctx);
}
}
@@ -0,0 +1,56 @@
/*
* 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.signalstrength;
import android.content.ComponentName;
import android.os.Build;
/**
* Constants for the signal sampler.
*
* @author Rodrigo Damazio
*/
public class SignalStrengthConstants {
// TODO: Many of these could be moved into a common library for third-party apps
public static final String START_TRACK_ACTION =
"com.google.android.apps.mytracks.TRACK_STARTED";
public static final String PAUSE_TRACK_ACTION =
"com.google.android.apps.mytracks.TRACK_PAUSED";
public static final String RESUME_TRACK_ACTION =
"com.google.android.apps.mytracks.TRACK_RESUMED";
public static final String STOP_TRACK_ACTION =
"com.google.android.apps.mytracks.TRACK_STOPPED";
public static final String TRACK_ID_EXTRA =
"com.google.android.apps.mytracks.TRACK_ID";
public static final String MYTRACKS_SERVICE =
"com.google.android.apps.mytracks.services.TrackRecordingService";
public static final String MYTRACKS_SERVICE_PACKAGE =
"com.google.android.maps.mytracks";
public static final ComponentName MYTRACKS_SERVICE_COMPONENT =
new ComponentName(MYTRACKS_SERVICE_PACKAGE, MYTRACKS_SERVICE);
public static final String START_SAMPLING =
"com.google.android.apps.mytracks.signalstrength.START";
public static final String STOP_SAMPLING =
"com.google.android.apps.mytracks.signalstrength.STOP";
public static final int ANDROID_API_LEVEL = Integer.parseInt(
Build.VERSION.SDK);
public static final String TAG = "SignalStrengthSampler";
private SignalStrengthConstants() {}
}
@@ -0,0 +1,42 @@
/*
* 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.signalstrength;
/**
* Interface for the service that reads signal strength.
*
* @author Rodrigo Damazio
*/
public interface SignalStrengthListener {
/**
* Interface for getting notified about a new sampled signal strength.
*/
public interface SignalStrengthCallback {
void onSignalStrengthSampled(
String description,
String icon);
}
/**
* Starts listening to signal strength.
*/
void register();
/**
* Stops listening to signal strength.
*/
void unregister();
}
@@ -0,0 +1,178 @@
/*
* 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.signalstrength;
import static com.google.android.apps.mytracks.signalstrength.SignalStrengthConstants.*;
import android.content.Context;
import android.telephony.CellLocation;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import java.util.List;
/**
* A class to monitor the network signal strength.
*
* TODO: i18n
*
* @author Sandor Dornbush
*/
public class SignalStrengthListenerCupcake extends PhoneStateListener
implements SignalStrengthListener {
private final Context context;
private final SignalStrengthCallback callback;
private TelephonyManager manager;
private int signalStrength = -1;
public SignalStrengthListenerCupcake(Context context, SignalStrengthCallback callback) {
this.context = context;
this.callback = callback;
}
@Override
public void register() {
manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (manager == null) {
Log.e(TAG, "Cannot get telephony manager.");
} else {
manager.listen(this, getListenEvents());
}
}
protected int getListenEvents() {
return PhoneStateListener.LISTEN_SIGNAL_STRENGTH;
}
@Override
public void onSignalStrengthChanged(int signalStrength) {
Log.d(TAG, "Signal Strength: " + signalStrength);
this.signalStrength = signalStrength;
notifySignalSampled();
}
protected void notifySignalSampled() {
int networkType = manager.getNetworkType();
callback.onSignalStrengthSampled(getDescription(), getIcon(networkType));
}
/**
* Gets a human readable description for the network type.
*
* @param type The integer constant for the network type
* @return A human readable description of the network type
*/
protected String getTypeAsString(int type) {
switch (type) {
case TelephonyManager.NETWORK_TYPE_EDGE:
return "EDGE";
case TelephonyManager.NETWORK_TYPE_GPRS:
return "GPRS";
case TelephonyManager.NETWORK_TYPE_UMTS:
return "UMTS";
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return "UNKNOWN";
}
}
/**
* Builds a description for the current signal strength.
*
* @return A human readable description of the network state
*/
private String getDescription() {
StringBuffer sb = new StringBuffer();
sb.append(getStrengthAsString());
sb.append("Network Type: ");
sb.append(getTypeAsString(manager.getNetworkType()));
sb.append('\n');
sb.append("Operator: ");
sb.append(manager.getNetworkOperatorName());
sb.append(" / ");
sb.append(manager.getNetworkOperator());
sb.append('\n');
sb.append("Roaming: ");
sb.append(manager.isNetworkRoaming());
sb.append('\n');
List<NeighboringCellInfo> infos = manager.getNeighboringCellInfo();
Log.i(TAG, "Found " + infos.size() + " cells.");
if (infos.size() > 0) {
sb.append("Neighbors: ");
for (NeighboringCellInfo info : infos) {
sb.append(info.toString());
sb.append(' ');
}
sb.append('\n');
}
CellLocation cell = manager.getCellLocation();
if (cell != null) {
sb.append("Cell: ");
sb.append(cell.toString());
sb.append('\n');
}
return sb.toString();
}
/**
* Gets the url for the waypoint icon for the current network type.
*
* @param type The network type
* @return A url to a image to use as the waypoint icon
*/
protected String getIcon(int type) {
switch (type) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
return "http://maps.google.com/mapfiles/ms/micons/green.png";
case TelephonyManager.NETWORK_TYPE_UMTS:
return "http://maps.google.com/mapfiles/ms/micons/blue.png";
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return "http://maps.google.com/mapfiles/ms/micons/red.png";
}
}
@Override
public void unregister() {
if (manager != null) {
manager.listen(this, PhoneStateListener.LISTEN_NONE);
manager = null;
}
}
public String getStrengthAsString() {
return "Strength: " + signalStrength + "\n";
}
protected Context getContext() {
return context;
}
public SignalStrengthCallback getSignalStrengthCallback() {
return callback;
}
}
@@ -0,0 +1,157 @@
/*
* 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.signalstrength;
import static com.google.android.apps.mytracks.signalstrength.SignalStrengthConstants.*;
import android.content.Context;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Log;
/**
* A class to monitor the network signal strength.
*
* TODO: i18n
*
* @author Sandor Dornbush
*/
public class SignalStrengthListenerEclair extends SignalStrengthListenerCupcake {
private SignalStrength signalStrength = null;
public SignalStrengthListenerEclair(Context ctx, SignalStrengthCallback callback) {
super(ctx, callback);
}
@Override
protected int getListenEvents() {
return PhoneStateListener.LISTEN_SIGNAL_STRENGTHS;
}
@Override
public void onSignalStrengthsChanged(SignalStrength signalStrength) {
Log.d(TAG, "Signal Strength Modern: " + signalStrength);
this.signalStrength = signalStrength;
notifySignalSampled();
}
/**
* Gets a human readable description for the network type.
*
* @param type The integer constant for the network type
* @return A human readable description of the network type
*/
@Override
protected String getTypeAsString(int type) {
switch (type) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
return "1xRTT";
case TelephonyManager.NETWORK_TYPE_CDMA:
return "CDMA";
case TelephonyManager.NETWORK_TYPE_EDGE:
return "EDGE";
case TelephonyManager.NETWORK_TYPE_EVDO_0:
return "EVDO 0";
case TelephonyManager.NETWORK_TYPE_EVDO_A:
return "EVDO A";
case TelephonyManager.NETWORK_TYPE_GPRS:
return "GPRS";
case TelephonyManager.NETWORK_TYPE_HSDPA:
return "HSDPA";
case TelephonyManager.NETWORK_TYPE_HSPA:
return "HSPA";
case TelephonyManager.NETWORK_TYPE_HSUPA:
return "HSUPA";
case TelephonyManager.NETWORK_TYPE_UMTS:
return "UMTS";
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return "UNKNOWN";
}
}
/**
* Gets the url for the waypoint icon for the current network type.
*
* @param type The network type
* @return A url to a image to use as the waypoint icon
*/
@Override
protected String getIcon(int type) {
switch (type) {
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
return "http://maps.google.com/mapfiles/ms/micons/green.png";
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_UMTS:
return "http://maps.google.com/mapfiles/ms/micons/blue.png";
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
return "http://maps.google.com/mapfiles/ms/micons/red.png";
}
}
@Override
public String getStrengthAsString() {
if (signalStrength == null) {
return "Strength: " + getContext().getString(R.string.unknown) + "\n";
}
StringBuffer sb = new StringBuffer();
if (signalStrength.isGsm()) {
appendSignal(signalStrength.getGsmSignalStrength(),
R.string.gsm_strength,
sb);
maybeAppendSignal(signalStrength.getGsmBitErrorRate(),
R.string.error_rate,
sb);
} else {
appendSignal(signalStrength.getCdmaDbm(), R.string.cdma_strength, sb);
appendSignal(signalStrength.getCdmaEcio() / 10.0, R.string.ecio, sb);
appendSignal(signalStrength.getEvdoDbm(), R.string.evdo_strength, sb);
appendSignal(signalStrength.getEvdoEcio() / 10.0, R.string.ecio, sb);
appendSignal(signalStrength.getEvdoSnr(),
R.string.signal_to_noise_ratio,
sb);
}
return sb.toString();
}
private void maybeAppendSignal(
int signal, int signalFormat, StringBuffer sb) {
if (signal > 0) {
sb.append(getContext().getString(signalFormat, signal));
}
}
private void appendSignal(int signal, int signalFormat, StringBuffer sb) {
sb.append(getContext().getString(signalFormat, signal));
sb.append("\n");
}
private void appendSignal(double signal, int signalFormat, StringBuffer sb) {
sb.append(getContext().getString(signalFormat, signal));
sb.append("\n");
}
}
@@ -0,0 +1,47 @@
/*
* 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.signalstrength;
import static com.google.android.apps.mytracks.signalstrength.SignalStrengthConstants.*;
import com.google.android.apps.mytracks.signalstrength.SignalStrengthListener.SignalStrengthCallback;
import android.content.Context;
import android.util.Log;
/**
* Factory for producing a proper {@link SignalStrengthListenerCupcake} according to the
* current API level.
*
* @author Rodrigo Damazio
*/
public class SignalStrengthListenerFactory {
public SignalStrengthListener create(Context context, SignalStrengthCallback callback) {
if (hasModernSignalStrength()) {
Log.d(TAG, "TrackRecordingService using modern signal strength api.");
return new SignalStrengthListenerEclair(context, callback);
} else {
Log.w(TAG, "TrackRecordingService using legacy signal strength api.");
return new SignalStrengthListenerCupcake(context, callback);
}
}
// @VisibleForTesting
protected boolean hasModernSignalStrength() {
return ANDROID_API_LEVEL >= 7;
}
}
@@ -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.signalstrength;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
/**
* Main signal strength sampler activity, which displays preferences.
*
* @author Rodrigo Damazio
*/
public class SignalStrengthPreferences extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
// Attach service control funciontality
findPreference(getString(R.string.settings_control_start_key))
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SignalStrengthService.startService(SignalStrengthPreferences.this);
return true;
}
});
findPreference(getString(R.string.settings_control_stop_key))
.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SignalStrengthService.stopService(SignalStrengthPreferences.this);
return true;
}
});
}
}
@@ -0,0 +1,248 @@
/*
* 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.signalstrength;
import static com.google.android.apps.mytracks.signalstrength.SignalStrengthConstants.*;
import com.google.android.apps.mytracks.content.Waypoint;
import com.google.android.apps.mytracks.services.ITrackRecordingService;
import com.google.android.apps.mytracks.signalstrength.SignalStrengthListener.SignalStrengthCallback;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import java.util.List;
/**
* Serivce which actually reads signal strength and sends it to My Tracks.
*
* @author Rodrigo Damazio
*/
public class SignalStrengthService extends Service
implements ServiceConnection, SignalStrengthCallback, OnSharedPreferenceChangeListener {
private SignalStrengthListener signalListener;
private ITrackRecordingService mytracksService;
private long lastSamplingTime;
private long samplingPeriod;
@Override
public void onStart(Intent intent, int startId) {
handleCommand(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent, startId);
return START_STICKY;
}
private void handleCommand(Intent intent, int startId) {
String action = intent.getAction();
if (START_SAMPLING.equals(action)) {
startSampling();
} else {
stopSampling();
}
}
private void startSampling() {
// TODO: Start foreground
if (!isMytracksRunning()) {
Log.w(TAG, "My Tracks not running!");
return;
}
Log.d(TAG, "Starting sampling");
Intent intent = new Intent();
intent.setComponent(MYTRACKS_SERVICE_COMPONENT);
if (!bindService(intent, SignalStrengthService.this, 0)) {
Log.e(TAG, "Couldn't bind to service.");
return;
}
}
private boolean isMytracksRunning() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (RunningServiceInfo serviceInfo : services) {
if (serviceInfo.pid != 0 &&
serviceInfo.service.equals(MYTRACKS_SERVICE_COMPONENT)) {
return true;
}
}
return false;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (this) {
mytracksService = ITrackRecordingService.Stub.asInterface(service);
Log.d(TAG, "Bound to My Tracks");
boolean recording = false;
try {
recording = mytracksService.isRecording();
} catch (RemoteException e) {
Log.e(TAG, "Failed to talk to my tracks", e);
}
if (!recording) {
Log.w(TAG, "My Tracks is not recording, bailing");
stopSampling();
return;
}
// We're ready to send waypoints, so register for signal sampling
SignalStrengthListenerFactory factory = new SignalStrengthListenerFactory();
signalListener = factory.create(this, this);
signalListener.register();
// Register for preference changes
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
samplingPeriod = Long.parseLong(preferences.getString(
getString(R.string.settings_min_signal_sampling_period_key), "-1"));
preferences.registerOnSharedPreferenceChangeListener(this);
// Tell the user we've started.
Toast.makeText(this, R.string.started_sampling, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onSignalStrengthSampled(String description, String icon) {
long now = System.currentTimeMillis();
if (now - lastSamplingTime < samplingPeriod * 60 * 1000) {
return;
}
// We don't set location or track id - My Tracks can fill those
Waypoint wpt = new Waypoint();
wpt.setName("Signal Strength");
wpt.setType(Waypoint.TYPE_WAYPOINT);
wpt.setIcon(icon);
wpt.setDescription(description);
try {
long waypointId;
synchronized (this) {
if (mytracksService == null) {
Log.d(TAG, "No my tracks service to send to");
return;
}
waypointId = mytracksService.insertWaypointMarker(wpt);
}
if (waypointId >= 0) {
Log.d(TAG, "Added signal marker");
lastSamplingTime = now;
} else {
Log.e(TAG, "Cannot insert waypoint marker?");
}
} catch (RemoteException e) {
Log.e(TAG, "Cannot talk to my tracks service", e);
}
}
private void stopSampling() {
Log.d(TAG, "Stopping sampling");
synchronized (this) {
// Unregister from preference change updates
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
preferences.unregisterOnSharedPreferenceChangeListener(this);
// Unregister from receiving signal updates
if (signalListener != null) {
signalListener.unregister();
signalListener = null;
}
// Unbind from My Tracks
if (mytracksService != null) {
unbindService(this);
mytracksService = null;
}
// Reset the last sampling time
lastSamplingTime = 0;
// Tell the user we've stopped
Toast.makeText(this, R.string.stopped_sampling, Toast.LENGTH_SHORT).show();
// Stop
stopSelf();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "Disconnected from My Tracks");
synchronized (this) {
mytracksService = null;
}
}
@Override
public void onDestroy() {
stopSampling();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static void startService(Context context) {
Intent intent = new Intent();
intent.setClass(context, SignalStrengthService.class);
intent.setAction(START_SAMPLING);
context.startService(intent);
}
public static void stopService(Context context) {
Intent intent = new Intent();
intent.setClass(context, SignalStrengthService.class);
intent.setAction(STOP_SAMPLING);
context.startService(intent);
}
@Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (getString(R.string.settings_min_signal_sampling_period_key).equals(key)) {
samplingPeriod = Long.parseLong(sharedPreferences.getString(key, "-1"));
}
}
}