Edit: I have added the code at the bottom of my most recent attempt.
I am trying to follow the example code here. I am having difficulty retrieving the last known location. I've tried several ways.
- In the linked example above, they use "GPSTracker". It gives there error "Cannot resolve symbol 'GPSTracker'". Is there an import I am missing for it?
- I tried to get the location using LocationManager as shown in this example but I get the error "Cannot resolve method 'requestLocationUpdates(java.lang.String, long, long, anonymous java.util.TimerTask)'". I also tried changing the third parameter to a float and got a similar error.
- I tried to get the location with FusedLocationProviderApi. I added the new GoogleApiClient.Builder to my onCreate() method, imported com.google.android.gms.location.FusedLocationProviderApi, and updated the build.gradle file. But it would give errors in the new GoogleApiClient.Builder method when I added "this" to .addConnectionCallbacks(this) and .addOnConnectionFailedListener(this). From what I've read, this is the best option as it is optimized for minimal battery use?
I am sort of at a loss on how to get the location. My goal is similar to the first linked example. I want to build a service that runs in the background and will send updates of it's location to a server on a specified interval. So far I have created two buttons which allow the service to start and stop. Once the service starts it checks if the device has mobile data. I then want it to check if location is turned on and has a GPS signal. I only want GPS signal and not also network location. Finally, if both data and location/GPS signal are true, then it beings to send it's location to the server during the specified interval. Here is my code so far.
MainActivity.java
package com.testtracker;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyService.setUp(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
public void startService(View view)
{
startService(new Intent(getBaseContext(), MyService.class));
}
public void stopService(View view)
{
stopService(new Intent(getBaseContext(), MyService.class));
}
}
MyService.java
package com.testtracker;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.os.IBinder;
import android.os.PowerManager;
import android.widget.Button;
import android.widget.Toast;
import java.lang.reflect.Method;
import java.util.Timer;
import java.util.TimerTask;
public class MyService extends Service
{
PowerManager powerManager;
PowerManager.WakeLock wakeLock;
static Button startButton;
static Button stopButton;
public static void setUp(Activity act)
{
startButton = (Button)act.findViewById(R.id.button2);
stopButton = (Button)act.findViewById(R.id.button);
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
startButton.setEnabled(false);
stopButton.setEnabled(true);
final ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Timer timer = new Timer();
final int TIME_INTERVAL = 10000;
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
timer.schedule(new TimerTask()
{
boolean mobileDataEnabled = false;
public void run() {
try {
//Check if phone has data
Class cmClass = Class.forName(connectivityManager.getClass().getName());
Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
method.setAccessible(true);
mobileDataEnabled = (Boolean) method.invoke(connectivityManager);
if (mobileDataEnabled)
{
//Has data. Check if phone has location and GPS signal. If it does, get location and send to server.
}
else
{
//No data. Check again during the next timer interval.
}
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, TIME_INTERVAL);
return START_STICKY;
}
@Override
public void onDestroy()
{
super.onDestroy();
wakeLock.release();
startButton.setEnabled(true);
stopButton.setEnabled(false);
Toast.makeText(this, "Service Stopped", Toast.LENGTH_SHORT).show();
}
}
Edit:
With the attempts I've been making (documented in my below comments), I figured I'd post my updated code. I'll keep the below code updated with my current attempt and leave the above code as my original. I would ideally like to use either FusedLocationProviderApi or Little Fluffy Location Library as they appear to be optimized for running in the background.
MainActivity.java
package com.testtracker;
import android.app.Activity;
import android.content.Intent;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity implements Button.OnClickListener
{
Button startButton;
Button stopButton;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button)findViewById(R.id.button2);
stopButton = (Button)findViewById(R.id.button);
startButton.setEnabled(true);
stopButton.setEnabled(false);
LocationManager service = (LocationManager)getSystemService(LOCATION_SERVICE);
boolean locationEnabled = service.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(!locationEnabled)
{
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.button2:
startButton.setEnabled(false);
stopButton.setEnabled(true);
Intent intentStart = new Intent(this, MyService.class);
startService(intentStart);
break;
case R.id.button:
startButton.setEnabled(true);
stopButton.setEnabled(false);
Intent intentStop = new Intent(this, MyService.class);
stopService(intentStop);
break;
}
}
}
MyService.java
package com.testtracker;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.widget.Toast;
import java.lang.reflect.Method;
import java.util.Timer;
import java.util.TimerTask;
public class MyService extends Service implements LocationListener
{
protected LocationManager locationManager;
protected LocationListener locationListener;
PowerManager powerManager;
PowerManager.WakeLock wakeLock;
public void onCreate()
{
Toast.makeText(this, "in on create", Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
final ConnectivityManager connectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
Timer timer = new Timer();
final int TIME_INTERVAL = 10000;
Looper.prepare(); //Fatal error said this must be added?
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
timer.schedule(new TimerTask()
{
boolean mobileDataEnabled = false;
public void run() {
try {
//Check if phone has data
Class cmClass = Class.forName(connectivityManager.getClass().getName());
Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
method.setAccessible(true);
mobileDataEnabled = (Boolean) method.invoke(connectivityManager);
if (mobileDataEnabled)
{
//Has data. Check if phone has location and GPS signal. If it does, get location and send to server.
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
final Criteria cri = new Criteria();
String bestProvider = locationManager.getBestProvider(cri, true);
Location loc = locationManager.getLastKnownLocation(bestProvider);
double lat = loc.getLatitude();
double lng = loc.getLongitude();
Toast.makeText(getBaseContext(), lat + " - " + lng, Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
onDestroy();
}
}
}, 0, TIME_INTERVAL);
return START_STICKY;
}
@Override
public void onDestroy()
{
super.onDestroy();
wakeLock.release();
Toast.makeText(this, "Service Stopped", Toast.LENGTH_SHORT).show();
}
@Override
public void onLocationChanged(Location location) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onProviderDisabled(String provider) {
}
}
logcat errors look like they are caused by Looper.prepare(). If I don't add that then I get the fatal error "Can't create handler inside thread that has not called Looper.prepare()". Can't win on this one?
2263-2263/com.testtracker E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.testtracker, PID: 2263
java.lang.RuntimeException: Unable to start service com.testtracker.MyService@18edb4e4 with Intent { cmp=com.testtracker/.MyService }: java.lang.RuntimeException: Only one Looper may be created per thread
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2914)
at android.app.ActivityThread.access$2100(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1401)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.RuntimeException: Only one Looper may be created per thread
at android.os.Looper.prepare(Looper.java:76)
at android.os.Looper.prepare(Looper.java:71)
at com.testtracker.MyService.onStartCommand(MyService.java:53)
at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:2897)
at android.app.ActivityThread.access$2100(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1401)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)