public class MainActivity extends AppCompatActivity implements View.OnClickListener, SensorEventListener {
private static final String LOG_TAG = "MainActivity";
private static final int SENSITIVITY = 300;
private static final int SPEED_MULTIPLIER = 10000;
private static final int MOVEMENT_TIMEOUT = 100;
private SensorManager mSensorManager;
private Sensor mAccelerationSensor;
private TransitionDrawable mTransitionDrawable;
private TextView vTextLatitude;
private TextView vTextLongitude;
private TextView vTextAddress;
private boolean mColorTransitionReverseDirection;
private long mLastTime = 0;
private float mLastX;
private float mLastY;
private float mLastZ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOG_TAG, "onCreate");
setContentView(R.layout.activity_main);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerationSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerationSensor, SensorManager.SENSOR_DELAY_UI);
RelativeLayout mainLayout = (RelativeLayout) findViewById(R.id.layout_mainPage);
Button vBtnLocation = (Button) findViewById(R.id.btn_locationUpdate);
vBtnLocation.setOnClickListener(this); // check onClick() method in class
vTextLatitude = (TextView) findViewById(R.id.text_latitudeValue);
vTextLongitude = (TextView) findViewById(R.id.text_longitudeValue);
vTextAddress = (TextView) findViewById(R.id.text_addressValue);
Intent locationIntent = new Intent(this, LocationUpdateService.class);
startService(locationIntent);
mTransitionDrawable = (TransitionDrawable) mainLayout.getBackground();
}
}
The sensors of the system are quite sensitive. If holding, you will find that it is always in motion, even if the hand is steady. Such data should be omitted. We need to store the exact time of the system presented in milliseconds to see how much time has passed since onSensorChanged has been invoked. We start with 100 milliseconds.
@Override
public void onSensorChanged(SensorEvent event) {
Sensor sensor = event.sensor;
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
Log.d(LOG_TAG, "Coordinates: x=" + x + ", y=" + y + ", z=" + z);
long now = System.currentTimeMillis();
if ((now - mLastTime) > MOVEMENT_TIMEOUT) {
long timePassed = (now - mLastTime);
mLastTime = now;
double speed = Math.sqrt(Math.pow(x - mLastX, 2) + Math.pow(y - mLastY, 2) + Math.pow(z - mLastZ, 2))/ timePassed * SPEED_MULTIPLIER;
if (speed > SENSITIVITY) {
Log.d(LOG_TAG, "onSensorChanged enough");
if (mColorTransitionReverseDirection) {
mTransitionDrawable.reverseTransition(1000);
} else {
mTransitionDrawable.startTransition(1000);
}
mColorTransitionReverseDirection = !mColorTransitionReverseDirection;
}
// saving last coordinates
mLastX = x;
mLastY = y;
mLastZ = z;
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) { /* do nothing */}
It is wise to keep the sensor unregistered for the time when the app hibernates in order to save battery power. Now insert these functions to the MainActivity class:
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, mAccelerationSensor, SensorManager.SENSOR_DELAY_UI);
}
To improve the readability onClick method is carried separately. The method is used to get coordinates and location addresses when clicked on a suitable button.
@Override
public void onClick(View v) {
Log.d(LOG_TAG, "onClick");
if (LocationUpdateService.sLastLocation != null) {
double latitude = LocationUpdateService.sLastLocation.getLatitude();
double longitude = LocationUpdateService.sLastLocation.getLongitude();
vTextLatitude.setText(String.valueOf(latitude));
vTextLongitude.setText(String.valueOf(longitude));
// try to get address by coordinates
Geocoder gCoder = new Geocoder(this, Locale.ENGLISH);
try {
// we don't need more than 1 result
List<Address> addressList = gCoder.getFromLocation(latitude, longitude, 1);
if (addressList != null && !addressList.isEmpty()) {
// we use StringBuilder to ease multiple string concatenation
StringBuilder addressBuilder = new StringBuilder();
Address resultAddress = addressList.get(0);
for (int i = 0; i < resultAddress.getMaxAddressLineIndex(); i++) {
addressBuilder.append(resultAddress.getAddressLine(i));
addressBuilder.append(" ");
}
vTextAddress.setText(addressBuilder.toString());
} else {
Log.e(LOG_TAG, "There's no appropriate address for such location");
}
} catch (IOException e) {
Log.e(LOG_TAG, "Error in Geocoder work " + e);
}
}
}
The Android sensor allows us to check real-time location of the user in the main plain locations.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.androidsensorabilities" >
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<application
...
</application>
</manifest>
We will make our location calculations based on the background service. Create a new LocationUpdateService.javapublic class LocationUpdateService extends Service {
private static final String LOG_TAG = "LocationUpdateService";
private static final int TIME_DIFF = 1000; // in millis
private static final float DISTANCE_DIFF = 10; // in meters
public static Location sLastLocation; // store last location info we got
private LocationManager mLocationManager;
private LocationListener mNetworkLocationListener = new LocationListener(LocationManager.NETWORK_PROVIDER);
private LocationListener mGpsLocationListener = new LocationListener(LocationManager.GPS_PROVIDER);
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d(LOG_TAG, "onCreate");
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, TIME_DIFF, DISTANCE_DIFF, mGpsLocationListener);
} catch (java.lang.SecurityException e) {
Log.w(LOG_TAG, "Cannot request location", e);
} catch (IllegalArgumentException e) {
Log.w(LOG_TAG, "GPS provider is disabled", e);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, TIME_DIFF, DISTANCE_DIFF, mNetworkLocationListener);
} catch (java.lang.SecurityException e) {
Log.w(LOG_TAG, "Cannot request location", e);
} catch (IllegalArgumentException e) {
Log.w(LOG_TAG, "Network provider is disabled", e);
}
}
@Override
public void onDestroy() {
Log.d(LOG_TAG, "onDestroy");
if (mLocationManager != null) {
mLocationManager.removeUpdates(mGpsLocationListener);
mLocationManager.removeUpdates(mNetworkLocationListener);
}
super.onDestroy();
}
}
We are adding the LocationListener internal class, which will respond to the changes in location and save the received data for exploitation.
private class LocationListener implements android.location.LocationListener {
public LocationListener(String provider) {
sLastLocation = new Location(provider);
}
@Override
public void onLocationChanged(Location location) {
Log.d(LOG_TAG, "onLocationChanged: latitude=" + location.getLatitude() + ", longitude=" + location.getLongitude());
// saving current location
sLastLocation.set(location);
}
@Override
public void onProviderDisabled(String provider) { }
@Override
public void onProviderEnabled(String provider) { }
@Override
public void onStatusChanged(String provider, int status, Bundle extras) { }
}
One more important thing to keep in mind is to use a real smartphone not the emulator when testing the application. Make sure you shake the device to check the accelerometer and click on the button which updates the phone in order to receive the data on the current location of the tracked phone. If having troubles viewing location, enable ‘high accuracy’.
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (0)