I have been working on a project where I need a button on a mobile to start up an activity on the watch. I have been going through the data layer sample in the sdk but can not get it working. I set up a wearable listener service class, but it is not picking up any messages. The service is added to the manifest but it is still not working. I do have other services too and I am thinking I might have too many services.
On the Wear watch, does an activity have to be running in order for it to start another activity? I want the watch to run nothing until it receives a message, is this possible?
Also, what should my edit configuration settings for wear module be? (eg, do not launch activity, launch default or launch from activity) I just want the wearable to boot up when a message is received.
If anybody can point me in the right direction, it would be hugely appreciated.
Thanks.
Moblie
Accessing the Wearable Data Layer
MainActivity.java
public class MainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener{
private GoogleApiClient mGoogleApiClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(Wearable.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
#Override
public void onConnected(Bundle bundle) {
Log.d("GoogleApi", "onConnected: " + bundle);
}
#Override
public void onConnectionSuspended(int i) {
Log.d("GoogleApi", "onConnectionSuspended: " + i);
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.d("GoogleApi", "onConnectionFailed: " + connectionResult);
}
GetConnectedNodes
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
for (Node node : getConnectedNodesResult.getNodes()) {
sendMessage(node.getId());
}
}
});
SendMessage
public static final String START_ACTIVITY_PATH = "/start/MainActivity";
private void sendMessage(String node) {
Wearable.MessageApi.sendMessage(mGoogleApiClient , node , START_ACTIVITY_PATH , new byte[0]).setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e("GoogleApi", "Failed to send message with status code: "
+ sendMessageResult.getStatus().getStatusCode());
}
}
});
}
Wear
Implement a Message Listener
WearDataLayerListenerService.java
public class WearDataLayerListenerService extends WearableListenerService {
public static final String START_ACTIVITY_PATH = "/start/MainActivity";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
if(messageEvent.getPath().equals(START_ACTIVITY_PATH)){
Intent intent = new Intent(this , MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}}
Add Listener Service to Manifest
<service
android:name=".WearDataLayerListenerService">
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
Related
I tried implementing the Activity Fence using the Google Awareness API. But changes in the user's activity are not getting detected. The headphone fence works as expected though.
ActivityFenceActivity
public class ActivityFenceActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks {
private static final String FENCE_RECEIVER_ACTION = "FENCE_RECEIVE";
private static final String FENCE_WALKING_KEY = "walkingKey";
private static final String FENCE_RUNNING_KEY = "runningKey";
private static final String TAG = ActivityFenceActivity.class.getSimpleName();
private GoogleApiClient googleApiClient;
private TextView activityTextView;
private BroadcastReceiver activityFenceReceiver = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
Toast.makeText(context, "Recieved", Toast.LENGTH_SHORT).show();
FenceState fenceState = FenceState.extract(intent);
if (TextUtils.equals(fenceState.getFenceKey(), FENCE_WALKING_KEY)) {
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
activityTextView.setText("User is walking");
break;
case FenceState.FALSE:
activityTextView.setText("User is not walking");
break;
case FenceState.UNKNOWN:
activityTextView.setText("Activity state unknown");
break;
}
} else if (TextUtils.equals(fenceState.getFenceKey(), FENCE_RUNNING_KEY)) {
switch (fenceState.getCurrentState()) {
case FenceState.TRUE:
activityTextView.setText("User is running");
break;
case FenceState.FALSE:
activityTextView.setText("User is not running");
break;
case FenceState.UNKNOWN:
activityTextView.setText("Activity state unknown");
break;
}
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activity_fence);
activityTextView = (TextView) findViewById(R.id.activityTextView);
googleApiClient = new GoogleApiClient.Builder(ActivityFenceActivity.this)
.addApi(Awareness.API)
.addConnectionCallbacks(this)
.build();
googleApiClient.connect();
findViewById(R.id.register_fence).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
registerActivityFence();
}
});
findViewById(R.id.unregister_fence).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
unregisterActivityFence();
}
});
}
#Override
public void onConnected(#Nullable final Bundle bundle) {
Log.d(TAG, "Google API connected");
}
#Override
public void onConnectionSuspended(final int i) {
Log.d(TAG, "Google API connection suspended");
}
#Override
protected void onStart() {
super.onStart();
registerReceiver(activityFenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
}
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(activityFenceReceiver);
unregisterActivityFence();
}
private void registerActivityFence() {
AwarenessFence walkingFence = DetectedActivityFence.during(DetectedActivityFence.WALKING);
AwarenessFence runningFence = DetectedActivityFence.during(DetectedActivityFence.RUNNING);
PendingIntent fencePendingIntent = PendingIntent.getBroadcast(this,
0,
new Intent(FENCE_RECEIVER_ACTION),
0);
Awareness.FenceApi.updateFences(googleApiClient, new FenceUpdateRequest.Builder()
.addFence(FENCE_WALKING_KEY, walkingFence, fencePendingIntent).build())
.setResultCallback(new ResultCallbacks<Status>() {
#Override
public void onSuccess(#NonNull final Status status) {
Toast.makeText(ActivityFenceActivity.this,
"Fence registered successfully",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(#NonNull final Status status) {
Toast.makeText(ActivityFenceActivity.this,
"Cannot register activity fence.",
Toast.LENGTH_SHORT).show();
}
});
Awareness.FenceApi.updateFences(googleApiClient, new FenceUpdateRequest.Builder()
.addFence(FENCE_RUNNING_KEY, runningFence, fencePendingIntent).build());
}
private void unregisterActivityFence() {
Awareness.FenceApi.updateFences(
googleApiClient,
new FenceUpdateRequest.Builder()
.removeFence(FENCE_WALKING_KEY)
.removeFence(FENCE_RUNNING_KEY)
.build()).setResultCallback(new ResultCallbacks<Status>() {
#Override
public void onSuccess(#NonNull Status status) {
Toast.makeText(ActivityFenceActivity.this,
"Fence unregistered successfully.",
Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(#NonNull Status status) {
Toast.makeText(ActivityFenceActivity.this,
"Cannot unregister headphone fence.",
Toast.LENGTH_SHORT).show();
}
});
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.subhrajyoti.awareness">
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.awareness.API_KEY"
android:value="AIzaSyBEmjlfC87xRUP2FnFynsDdY3QRuI1hIHs" />
</application>
</manifest>
If the headphone fence is working and the activity fence is not, maybe you simply forgot to add the permission in the manifest?
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/>
I tried to use Awareness API instead of geofencing, I think the problem is how you tested your code.
I'm pretty sure that Awareness uses phone's sensors, so when I use the GPS simulator to simulate a walk, it doesn't trigger anything, but if I use my real phone and walk, BroadcastReceiver is triggered.
However, I don't know how to simulate a "sensor walking" on the emulator!
Was working when I was using GoogleApiClient in an Activity but moved it to a Service and the onConnected is not being called.
public class StepsMonitoringService extends Service implements GoogleApiClient.ConnectionCallbacks {
private GoogleApiClient mClient;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mClient = new GoogleApiClient.Builder(this).addApi(Fitness.HISTORY_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
.addConnectionCallbacks(this).build();
mClient.connect();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
mClient.disconnect();
}
#Override
public void onConnected(Bundle connectionHint) {
// NOT being called!! WHY??
}
#Override
public void onConnectionSuspended(int cause) {
}
}
Any ideas anyone? What am I doing wrong? Has anyone got GoogleApiClient working in a Service?
The service is called from an Activity
public class MainActivity extends FragmentActivity {
private TextView mStepsView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, StepsMonitoringService.class);
startService(intent);
LocalBroadcastManager.getInstance(this).registerReceiver(mMessageReceiver, new IntentFilter("StepMonitoringServiceIntent"));
mStepsView = (TextView) findViewById(R.id.steps);
}
private void displayOnUI(String msg) {
mStepsView.setText(msg + "\n" + mStepsView.getText());
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
long steps = intent.getLongExtra("steps", 0);
displayOnUI(steps + " steps");
}
};
}
During debugging I can see the onCreate of the Service gets called, however the onConnected is never called. Nothing special in the logs from what I can see.
First implement these interfaces:
GoogleApiClient.ConnectionCallbacks
GoogleApiClient.OnConnectionFailedListener
Then you will have to add these methods to your class:
public void onConnected(final Bundle bundle)
public void onConnectionSuspended(final int i)
public void onConnectionFailed(final ConnectionResult connectionResult)
As soon as you connect, the OnConnected method will be called. In this method can you do whatever you want, like getting the current location, the location address, add a marker point to map, etc.
But you cannot do anything without establishing a connection to the Google API client.
To connect to the GoogleAPIClient, you can add this method to your class and call it in your onCreate method.
private synchronized void buildGoogleAPIClient(){
googleApiclient = new GoogleApiClient.Builder(getActivity())
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
googleApiclient.connect();
Toast.makeText(getActivity(), "connected", Toast.LENGTH_SHORT).show();
}
Add addOnConnectionFailedListener to GoogleApiClient to track the error. As per doc addOnConnectionFailedListener called when there was an error connecting the client to the service.
public class StepsMonitoringService extends Service implements GoogleApiClient.ConnectionCallbacks {
private GoogleApiClient mClient;
#Override
public void onCreate() {
super.onCreate();
mClient = new GoogleApiClient.Builder(this).addApi(Fitness.HISTORY_API)
.addScope(new Scope(Scopes.FITNESS_ACTIVITY_READ))
.addConnectionCallbacks(this)
//Add Connection Failed Listener to track error.
.addOnConnectionFailedListener(this)
.build();
mClient.connect();
}
#Override
public void onConnected(Bundle connectionHint) {
// NOT being called!! WHY??
}
#Override
public void onConnectionSuspended(int cause) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
//Called called when there was an error connecting the client to the service.
Log.i(LOG_TAG,"onConnectionFailed:"+connectionResult.getErrorCode()+","+connectionResult.getErrorMessage());
}
}
try this.
public class StepsMonitoringService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
/**
* Provides the entry point to Google Play services.
*/
protected GoogleApiClient mClient;
/**
* Builds a GoogleApiClient. Uses the addApi() method to request the
* Fitness API.
*/
protected synchronized void buildGoogleApiClient() {
mClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(Fitness.HISTORY_API)
.build();
}
#Override
public void onCreate() {
super.onCreate();
if (mClient == null) {
buildGoogleApiClient();
}
mClient.connect();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onConnected(Bundle connectionHint) {
// NOT being called!! WHY??
}
#Override
public void onConnectionSuspended(int cause) {
mClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Called called when there was an error connecting the client to the
// service.
Log.i(LOG_TAG,
"onConnectionFailed:" + connectionResult.getErrorCode() + "," + connectionResult.getErrorMessage());
}
}
I had a same issue when I was using useDefaultAccount() to create a client. I then replaced it with setAccountName() and it starts working. I think for some reasons accountpicker doesn't work in service that's why googleapiclient's connect() silently failed since you need specify which account you use for retrieving Fitness info. Noted that you need to have an account picker in your activity and pass the email account to your service in some way.
I'm trying to implement a feature which would read the notification and it seems that I've a bug in it because the onNotificationPosted() of the NotificationListenerService is never called. I configured the manifest, modified the security settings properly and even started the service manually but it did nothing. Can it be that testing it in an emulator is not the best way to proceed.
Here is the Notification getter class:
/**
* Created by laurentmeyer on 28/02/15.
*/
public class NotificationListener extends NotificationListenerService {
public NotificationListener(){
Log.d("Notification", "Created");
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
Notification mNotification = sbn.getNotification();
Intent intent = new Intent("Msg");
Log.d("Notification", "Received");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
#Override
public void onCreate() {
super.onCreate();
Log.d("Notification", "created");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("Notification", "destroyed");
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
}
}
(Sorry for the incorrect indentation)
This is my Fragment which should respond to the broadcasts:
public class VerificationFragment extends BaseFragment implements SMSReceivedCallbacks {
public void onNotificationReceived(int code) {
ed.setText("OK");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LocalBroadcastManager.getInstance(getActivity()).registerReceiver(new Receiver(), new IntentFilter("Msg"));
}
// A bunch of other useless stuff
private class Receiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
onNotificationReceived(0);
}
}
}
The Manifest:
<service
android:name=".NotificationListener"
android:label="#string/service_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
To summarise, I've:
02-28 11:40:53.236 6585-6585/com.******.laurentmeyer.****** D/Notificationīš Created
02-28 11:40:53.236 6585-6585/com.******.laurentmeyer.****** D/Notificationīš created
and then nothing. I already looked at all the threads on SO but maybe missing something really simple.
Enable Notification Access in your settings:
if (Settings.Secure.getString(this.getContentResolver(), "enabled_notification_listeners").contains(getApplicationContext().getPackageName())) {
//Add the code to launch the NotificationService Listener here.
} else {
//Launch notification access in the settings...
getApplicationContext().startActivity(new Intent(
"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
}
GooglePlayServices recently changed from the way you detect the users activity - from using ActivityRecognitionClient to ActivityRecognitionApi. I have followed all the examples I can find but must have missed something.
After making the call to GoogleApiClient.Builder(mContext).addApi(ActivityRecognition.API)...build() I was expecting that one of the connection callback methods would be called. I have logging and breakpoints in all the callback methods but none of them are ever called.
OnReceive() and CallClient() are called as expected and the manifest file includes: uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"
What am I missing? I would love to get to the bottom of this given the amount of time I have wasted so far.
(P.S. It's my own Logging library - it is not part of the issue)
public class BootAtStartupReceiver extends BroadcastReceiver implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener{
private final String TAG = getClass().getName();
private PendingIntent mpIntent;
private static GoogleApiClient mGoogleApiClient;
private Context mContext;
#Override
public void onReceive(Context context, Intent intent) {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside onReceive: " + intent.getAction());
if ((intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) || (intent.getAction().equals(BroadcastIntentConstants.ACTIVITY_FIRST_TIME_CALL_SERVICE)))
{
mContext = context;
callClient();
}
}
private void callClient() {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside callClient");
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mContext);
if (resp == ConnectionResult.SUCCESS) {
mGoogleApiClient = new GoogleApiClient.Builder(mContext)
.addApi(ActivityRecognition.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
} else {
Logging.log(Logging.enumLoggingLevel.e, getClass().getName(), "Google play Services are not available.");
}
}
#Override
public void onConnected(Bundle bundle) {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside onConnected");
Intent intent = new Intent(mContext, ActivityRecognitionIntentService.class);
mpIntent = PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mGoogleApiClient, 1000, mpIntent);
}
#Override
public void onDisconnected() {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside onDisconnected");
}
#Override
public void onConnectionSuspended(int i) {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside onConnectionSuspended");
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Logging.log(Logging.enumLoggingLevel.d,TAG,"inside onConnectionFailed");
}
}
Are you missing the call to connect() after the build? The callbacks dont fire unless you call connect.
mGoogleApiClient.connect()
This code is supposed to use a service to show a toast message. There are no errors, but it doesn't show the toast.
main activity
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent i= new Intent(this, BackgroundMusic.class);
this.startService(i);
}
}
service (its called Background Music but for now it is supposed to show a toast message)
public class BackgroundMusic extends IntentService {
public BackgroundMusic() {
super("BackgroundMusic");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
}
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.starwars"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:debuggable="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<service android:name=".BackgroundMusic" />
<activity
android:name="com.example.starwars.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:label="#string/app_name" android:name="BackgroundMusic"/>
</application>
</manifest>
Try this:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(YourService.this.getApplicationContext(),"My Awesome service toast...",Toast.LENGTH_SHORT).show();
}
});
See this part of the docs
(An IntentService has a few limitations:
It can't interact directly with your user interface. To put its
results in the UI, you have to send them to an Activity.
You need to put it on the main Thread. See the answer here by rony of a way to do that.
and from the full documentation on IntentService
handles each Intent in turn using a worker thread
It's probably best to delegate all GUI activities (including toast) to the Activity that is using your Service. For example, I have a bound service for doing location capture in the background and posting updates to the screen while my app is visible.
My app implements a simple interface:
public interface ICapture {
void update(Location location);
}
and my class def looks like this:
public class MyActivity extends Activity implements ICapture {
...
Here's the stuff for handling the bound service:
private CaptureService captureService;
private ServiceConnection captureServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
CaptureService.MyLocalBinder binder = (CaptureService.MyLocalBinder) service;
captureService = binder.getService();
captureService.setOwner(ICapture.this);
}
public void onServiceDisconnected(ComponentName arg0) {
}
};
The only thing here that's not standard is the line
captureService.setOwner(ICapture.this);
which provides the service with a reference to the app's implementation of ICapture. See below for how it's used.
I start the Service in onCreate():
Intent intent = new Intent(this, CaptureService.class);
startService(intent);
bindService(intent, captureServiceConnection, Context.BIND_AUTO_CREATE);
and I use these methods to tell the service when the app is visible and able to satisfy GUI requests:
#Override
public void onPause() {
super.onPause();
if (captureService != null) {
captureService.setOwner(null);
}
}
#Override
public void onResume() {
super.onResume();
if (captureService != null) {
captureService.setOwner(this);
}
}
The Service looks like this:
package *****;
import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class CaptureService extends Service implements
com.google.android.gms.location.LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final long UPDATE_INTERVAL = 1000 * 10;
private static final long FASTEST_INTERVAL = 1000 * 5;
private final IBinder myBinder = new MyLocalBinder();
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private ICapture owner;
#Override
public void onCreate() {
if (isGooglePlayServicesAvailable()) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mGoogleApiClient.connect();
}
}
#Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
}
/**************************************************************************
* The binder that returns the service activity.
*/
public class MyLocalBinder extends Binder {
public CaptureService getService() {
return CaptureService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return myBinder;
}
/**************************************************************************
* Bound methods.
*
* Set the owner, to be notified when the position changes.
*
* #param owner
*/
public void setOwner(ICapture owner) {
this.owner = owner;
}
/**************************************************************************
* Start the service and keep it running when the phone is idle.
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
/**
* Callback when the location changes. Inform the owner.
*
* #param location
*/
#Override
public void onLocationChanged(Location location) {
if (owner != null) {
owner.update(location);
}
}
private boolean isGooglePlayServicesAvailable() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status) {
return true;
} else {
return false;
}
}
}
All this is pretty standard code you can find elsewhere. The main thing is that when a location update occurs the code calls the app via its implemented ICapture interface, but only if the app is visible. The implementation of onPause() and onResume() in the app makes sure that the service knows when the app can accept calls.
To do a toast popup, add another method to the ICapture interface and implement it in the app. Your service can then call it any time it knows the screen can accept it. In fact, toast popups will still come through even when the app isn't in the foreground, but I believe they get blocked when the screen goes inactive, which in turn blocks the service. So it's better to only send them when the app is in the foreground.