I am following the android guide android guide to build a simple native bridge for react-native for geofencing.
But I do not get any response when entering or leaving a geofence. It seems like the PendingIntent / IntentService for Transitions is not running properly.
MyModule looks basically like this. It also creates mGeofenceList like in the docs populated with data from react-native.
MyModule:
public class MyModule extends ReactContextBaseJavaModule {
//Build geofences
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
builder.addGeofences(mGeofenceList);
return builder.build();
}
//Build pending intent
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
if (mGeofencePendingIntent != null) {
return mGeofencePendingIntent;
}
Intent intent = new Intent(reactContext, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
mGeofencePendingIntent = PendingIntent.getService(reactContext, 0, intent, PendingIntent.
FLAG_UPDATE_CURRENT);
return mGeofencePendingIntent;
}
#ReactMethod
public void startMonitoring() {
mGeofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "Start Monitoring");
postNotification("Start Monitoring", "Pressed Start Monitoring");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "Start Monitoring: " + e.getMessage());
}
});
}
}
When running startMonitoring(), the notification (Start Monitoring) and the log gets produced, so I assume that the error is not in this part.
The IntentService looks also pretty basic /similar to the docs.
IntentService:
public class GeofenceTransitionsIntentService extends IntentService {
private static final String TAG = "GeofenceService";
private Handler handler;
SharedPreferences sp;
public GeofenceTransitionsIntentService(){
super(TAG);
}
#Override
public void onCreate() {
super.onCreate();
sp = PreferenceManager.getDefaultSharedPreferences(this);
handler = new Handler();
Log.i(TAG, "Intent created");
}
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "onHandleIntent");
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = "Error Code: " + String.valueOf(geofencingEvent.getErrorCode());
Log.e(TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
// Get the geofences that were triggered. A single event can trigger
// multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = getGeofenceTransitionDetails(
geofenceTransition,
triggeringGeofences
);
// Send notification and log the transition details.
//sendNotification(geofenceTransitionDetails);
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Enter/Exit", Toast.LENGTH_SHORT).show();
}
});
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
Log.e(TAG, "Invalid transition");
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "ERROR", Toast.LENGTH_SHORT).show();
}
});
}
}
/*
Helpfunctions for logging
*/
private String getGeofenceTransitionDetails(
int geofenceTransition,
List<Geofence> triggeringGeofences) {
String geofenceTransitionString = getTransitionString(geofenceTransition);
// Get the Ids of each geofence that was triggered.
ArrayList<String> triggeringGeofencesIdsList = new ArrayList<>();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
}
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return "entered Geofence";
case Geofence.GEOFENCE_TRANSITION_EXIT:
return "exit Geofence";
default:
return "unknown Transition";
}
}
}
But none of the outputs of this class gets produced!
In the manifest of my native module I added the permission:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
And in the manifest of the TestApplication that uses this module I added this permission as well and in the application tag I added
<service android:name="com.mymodule.GeofenceTransitionsIntentService" android:exported="false"/>
I could not add this last line in the module's manifest, cause it was missing the application tag and has no activity. I am not sure if this is the right place.
I am testing in the emulator and change the location to a list of GPS data playback.
Questions
How can I verify that the ServiceIntent is running? Can I get Status of it?
Where does the logs appear? In com.TestApplication or somewhere else?
and of course:
3. Where is my error?
Ok, answering my own question, or specific only question 3:
The code above has no error, or at least and works as expected on a hardware device.
So, how to properly debug Geofencing on an emulator?
Related
So i've built a location-based reminder app using geofences.
Everything is working but one thing - the geofences are not triggering when the app is closed.
This kinda loses the entire idea of the app - sending notifications.
So i digged a bit and found out that if I open google maps, and press the GPS kinda thing to take the map to my location, the geofences trigger perfectly. But when I close it (or even put it in background), they won't trigger. I checked - and they get added like desired.
My question is - is this a known issue with the android studio emulator?
I'm using pixel 3 XL on API 33, if that is relevant.
Also, here's the broadcast receiver for my Geofences, if that is relevant too.
public class GeofenceBroadcastReceiver extends BroadcastReceiver
{
private static final String activityName = "GeofenceBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent)
{
Log.d(activityName, "Broadcast received: geofence triggered");
try
{
boolean isAppInForeground = new ForegroundCheckTask().execute(context).get();
if (isAppInForeground)
{
Log.d(activityName, "But app is not closed. Skip and do not send notification.");
return;
}
}
catch (Exception e)
{
e.printStackTrace();
}
NotificationHelper notificationHelper = new NotificationHelper(context);
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError())
{
Log.d(activityName, "onReceive: error");
return;
}
SharedPreferences sharedPref = context.getSharedPreferences("remindify_sp", MODE_PRIVATE);
String connectedUsername = sharedPref.getString("connected username", null);
List<Geofence> triggeredGeofences = geofencingEvent.getTriggeringGeofences();
ArrayList<LocationClass> locationsList = UtilityClass.getLocationListForUser(activityName, connectedUsername);
LocationClass notifiedLocation = findNotifiedLocationByGeofenceId(triggeredGeofences, locationsList);
int transitionType = geofencingEvent.getGeofenceTransition();
switch (transitionType)
{
case Geofence.GEOFENCE_TRANSITION_ENTER:
Toast.makeText(context, "Entered geofence", Toast.LENGTH_SHORT).show();
notificationHelper.sendHighPriorityNotification(
"Entered location: " + notifiedLocation.getName(),
"Click here to view it's task list!",
LoginActivity.class, notifiedLocation);
break;
case Geofence.GEOFENCE_TRANSITION_DWELL:
// Toast.makeText(context, "Dwelled geofence", Toast.LENGTH_SHORT).show();
break;
case Geofence.GEOFENCE_TRANSITION_EXIT:
// Toast.makeText(context, "Exited geofence", Toast.LENGTH_SHORT).show();
break;
}
}
private LocationClass findNotifiedLocationByGeofenceId(List<Geofence> triggeredGeofences,
ArrayList<LocationClass> locationsList)
{
Geofence geofence = triggeredGeofences.get(0);
String geofenceId = geofence.getRequestId();
for (LocationClass location : locationsList)
{
if (geofenceId.equals(String.valueOf(location.getId())))
{
// found location with corresponding geofence id
return location;
}
}
return null;
}
}
I've implemented some geofences into an app and have been testing them using mock locations on my test device (Samsung S4 running Android 5.0.1). While using mock locations, the transition detection has been working 100% of the time. I've now moved onto other Android devices and put the app on some phones which frequently (physically) enter and leave the geofence location, and I've noticed that the detection OFTEN doesn't work. This is really inconvenient so I was hoping there would be some way to make the detection more consistent.
My code which calls the geofence to be created:
private void startGeofenceMonitoring() {
Log.d(TAG, "startGeofenceMonitoring called");
try {
Geofence geofence = new Geofence.Builder()
.setRequestId(GEOFENCE_ID)
.setCircularRegion(51.364516, -0.189643, 150)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setNotificationResponsiveness(1000)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
.build();
GeofencingRequest geofencingRequest = new GeofencingRequest.Builder()
.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER | GeofencingRequest.INITIAL_TRIGGER_EXIT)
.addGeofence(geofence)
.build();
Intent intent = new Intent(this, GeofenceService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (!googleApiClient.isConnected()) {
Log.d(TAG, "GoogleApiClient is not connected");
} else {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MapsActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION);
return;
}
LocationServices.GeofencingApi.addGeofences(googleApiClient, geofencingRequest, pendingIntent)
.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(#NonNull Status status) {
if (status.isSuccess()) {
Log.d(TAG, "Successfully added Geofence");
} else {
Log.d(TAG, "Failed to add geofence - " + status.getStatus());
}
}
});
}
} catch (SecurityException e) {
Log.d(TAG, "SecurityException - " + e.getMessage());
}
}
GeofenceService.java:
public class GeofenceService extends IntentService {
public static final String TAG = "GeofenceService";
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
public GeofenceService() {
super(TAG);
}
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
if (event.hasError()) {
//TODO:
} else {
double longitude = 0;
double latitude = 0;
int transition = event.getGeofenceTransition();
List<Geofence> geofences = event.getTriggeringGeofences();
Geofence geofence = geofences.get(0);
String requestID = geofence.getRequestId();
longitude = event.getTriggeringLocation().getLongitude();
latitude = event.getTriggeringLocation().getLatitude();
final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (longitude != 0 && latitude != 0) {
if (transition == Geofence.GEOFENCE_TRANSITION_ENTER) {
Log.d(TAG, "Entering geofence - " + requestID);
//My on enter code
} else if (transition == Geofence.GEOFENCE_TRANSITION_EXIT) {
Log.d(TAG, "Exited geofence - " + requestID);
//My on exit code
}
} else {
mRootRef.child("users/" + user.getUid() + "/error").setValue("lat/long = 0");
}
}
}
I've seen online that say using a broadcast receiver works better than what I've done but embarrassingly, I'm not quite good enough to really understand how to convert what I have done to what others have suggested.
Thanks.
check out this answer there is an implementation of broadcast receiver with geofence
Android Geofencing Notifications when app is closed
i made a simple TCP-client and it's work fine when i use simple activity and button to send the message
now i implement geofence application using same google geofence example here
public class GeofenceTransitionsIntentService extends IntentService {
protected static final String TAG = "GeofenceTransitionsIS";
/**
* This constructor is required, and calls the super IntentService(String)
* constructor with the name for a worker thread.
*/
public GeofenceTransitionsIntentService() {
// Use the TAG to name the worker thread.
super(TAG);
}
#Override
public void onCreate() {
super.onCreate();
}
/**
* Handles incoming intents.
* #param intent sent by Location Services. This Intent is provided to Location
* Services (inside a PendingIntent) when addGeofences() is called.
*/
#Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
String errorMessage = GeofenceErrorMessages.getErrorString(this,
geofencingEvent.getErrorCode());
Log.e(TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
// Get the geofences that were triggered. A single event can trigger multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = getGeofenceTransitionDetails(
this,
geofenceTransition,
triggeringGeofences
);
// Send notification and log the transition details.
sendNotification(geofenceTransitionDetails);
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition));
}
}
/**
* Gets transition details and returns them as a formatted string.
*
* #param context The app context.
* #param geofenceTransition The ID of the geofence transition.
* #param triggeringGeofences The geofence(s) triggered.
* #return The transition details formatted as String.
*/
private String getGeofenceTransitionDetails(
Context context,
int geofenceTransition,
List<Geofence> triggeringGeofences) {
String geofenceTransitionString = getTransitionString(geofenceTransition);
// Get the Ids of each geofence that was triggered.
ArrayList triggeringGeofencesIdsList = new ArrayList();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
}
/**
* Posts a notification in the notification bar when a transition is detected.
* If the user clicks the notification, control goes to the MainActivity.
*/
private void sendNotification(String notificationDetails) {
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Add the main Activity to the task stack as the parent.
stackBuilder.addParentStack(MainActivity.class);
// Push the content Intent onto the stack.
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Define the notification settings.
builder.setSmallIcon(R.drawable.ic_launcher)
// In a real app, you may want to use a library like Volley
// to decode the Bitmap.
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText(getString(R.string.geofence_transition_notification_text))
.setContentIntent(notificationPendingIntent);
// Dismiss notification once the user touches it.
builder.setAutoCancel(true);
// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
/**
* Maps geofence transition types to their human-readable equivalents.
*
* #param transitionType A transition type constant defined in Geofence
* #return A String indicating the type of transition
*/
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return getString(R.string.geofence_transition_entered);
case Geofence.GEOFENCE_TRANSITION_EXIT:
return getString(R.string.geofence_transition_exited);
default:
return getString(R.string.unknown_geofence_transition);
}
}
}
what i want is to send a message to the server when ever enter/exit the geofence using tcp
i make something like this
start the connection inside on Create
#Override
public void onCreate() {
super.onCreate();
if (mTcpClient == null) {
new ConnectTask().execute("");
}
}
thread to start send message to the server
Thread thread=new Thread(){
#Override
public void run() {
try {
mTcpClient.sendMessage("halo server");
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();
it's work for first time enter the geofence and send the message successfully
but next time enter/exit will give error
java.lang.NullPointerException on the sendmessage method
i think the problem that i don't start and close the connection right, can someone help me ?
EDIT
AsyncTask class
public class ConnectTask extends AsyncTask<String, String, TCPClient> {
#Override
protected TCPClient doInBackground(String... message) {
// create a TCPClient object
mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
}
TCPclient class
public class TCPClient {
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
/**
* Constructor of the class.
*/
public TCPClient(OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* #param message text entered by client
*/
public void sendMessage(String message) {
if (mBufferOut != null && !mBufferOut.checkError()) {
mBufferOut.println(message);
mBufferOut.flush();
}
}
/**
* Close the connection and release the members
*/
public void stopClient() {
sendMessage("close"+"bye");
mRun = false;
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() {
mRun = true;
try {
InetAddress serverAddr = InetAddress.getByName(Constants.SERVER_IP);
Log.e("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, Constants.SERVER_PORT);
try {
//sends the message to the server
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//receives the message which the server sends back
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
sendMessage("name"+"hi");
while (mRun) {
mServerMessage = mBufferIn.readLine();
if (mServerMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(mServerMessage);
}
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
socket.close();
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
//Declare the interface.
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
well the solution was easy
i just convert my tcp object to static
old
private TCPClient mTcpClient;
new
private static TCPClient mTcpClient;
and really i dont understand why it's work as static, i will be thankful if someone can explain this
i'm trying to use FusedLocationApi with pending intent to get period location updates so i can send the data to some server for processing. Everything works. However, there are some instances where the broadcast just stops receiving. I would need to restart the service again in order for it to continue.
I already have the service onStartCommand to return START_STICKY so even if the app is killed it should start the service again. Furthermore, I also added a Boot Completed Intent Receiver so if the phone died and user restarted phone it would restart my service.
So it seems everything is fine and working but just at some point, everything just stops. I did notice a few times that when it does stop working, the last location i received was NULL (i log every location update and error messages throughout my project).
Any ideas why location services just stops working?
P.S. there is no connection failure because i put a message and hook into that function and it's not being called. And it's not internet failure either as i log that as well. Once internet is restored it would continue as normal/expected.
Thanks.
This is the main activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayShowHomeEnabled(false);
actionBar.setDisplayShowTitleEnabled(false);
if (findViewById(android.R.id.home) != null) {
findViewById(android.R.id.home).setVisibility(View.GONE);
}
LayoutInflater inflator = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflator.inflate(R.layout.header_logo, null);
ActionBar.LayoutParams params = new ActionBar.LayoutParams(ActionBar.LayoutParams.WRAP_CONTENT, ActionBar.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setCustomView(view, params);
}
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.status_text);
// init preferences and status handler
MyStatusHandler.init(getApplicationContext(), textView);
// init web service call class
...;
// check for location services
if (!isLocationEnabled(getApplicationContext())){
String msg = "Location services not turned on";
MyStatusHandler.setStatusText(msg);
}
}
public static boolean isLocationEnabled(Context context) {
int locationMode = 0;
String locationProviders;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
}else{
locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
#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) {
// create new intent activity for the settings page
Intent i = new Intent(this, MySettingsActivity.class);
startActivity(i);
return true;
}
else if (id == R.id.action_about){
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 2. Chain together various setter methods to set the dialog characteristics
if (Constants.DEBUG_BUILD == true) {
builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum() + " dev Build: " + MyStatusHandler.getDevVersionNum())
.setTitle("About")
.setCancelable(false)
.setPositiveButton("OK", null);
}
else{
builder.setMessage("v." + MyStatusHandler.getReleaseVersionNum())
.setTitle("About")
.setCancelable(false)
.setPositiveButton("OK", null);
}
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
// show it
dialog.show();
return true;
}
return super.onOptionsItemSelected(item);
}
// check email entered
public boolean isSettingsEntered(){
boolean result = true;
if (MyStatusHandler.getEmailText().equals("") || MyStatusHandler.getPasswordText().equals("")){
// 1. Instantiate an AlertDialog.Builder with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 2. Chain together various setter methods to set the dialog characteristics
builder.setMessage("Please ensure both email and password are entered in settings")
.setTitle("Email and/or Password not set")
.setCancelable(false)
.setPositiveButton("OK",null);
// 3. Get the AlertDialog from create()
AlertDialog dialog = builder.create();
// show it
dialog.show();
result = false;
}
return result;
}
/** Called when the user clicks the Opt in button */
public void startService(View view) {
// Do something in response to button
if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
// send opt in to web service
...;
// start service
startService(new Intent(this, BackgroundLocationService.class));
// update status text
String msg = "Connecting ...";
MyStatusHandler.setStatusText(msg);
}
}
/** Called when the user clicks the Opt out button */
public void stopService(View view) {
// Do something in response to button
if (isSettingsEntered() && isLocationEnabled(getApplicationContext())) {
// send OptOut to web service
...;
// update status text
String msg = "Connecting ...";
MyStatusHandler.setStatusText(msg);
}
}
public static void ...(boolean isOptIn, Location location, boolean sendOptIn){
if (sendOptIn)
{
// send opt in via async task
}
else{
// send location via async task
}
}
// Handle results returned to the FragmentActivity by Google Play services
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
case Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST :
// log the error
MyStatusHandler.logDataToFile("Connection Failure Resolution Request - Result Code: "+String.valueOf(resultCode));
// If the result code is Activity.RESULT_OK, try to connect again
switch (resultCode) {
case Activity.RESULT_OK :
// Try the request again
MyStatusHandler.logDataToFile("Attempting to re-start service");
// start service
startService(new Intent(this, BackgroundLocationService.class));
break;
}
}
}
}
Here is the background service:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
public static final String TAG = BackgroundLocationService.class.getSimpleName();
private GoogleApiClient mGoogleApiClient;
private boolean mInProgress;
private LocationRequest mLocationRequest;
public void onCreate(){
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
#Override
public int onStartCommand (Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if(mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
if(!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setFastestInterval(Constants.FASTEST_INTERVAL)
.setInterval(Constants.UPDATE_INTERVAL);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
new Intent(this, MyLocationHandler.class),
PendingIntent.FLAG_CANCEL_CURRENT);
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, pendingIntent);
}
else{
MyStatusHandler.setStatusText("Google Client Failed");
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
mInProgress = false;
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(null, Constants.CONNECTION_FAILURE_RESOLUTION_REQUEST);
// * Thrown if Google Play services canceled the original
// * PendingIntent
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
//* If no resolution is available, display a dialog to the
// * user with the error.
Log.i(TAG, "Location services connection failed with code " + connectionResult.getErrorCode());
MyStatusHandler.setStatusText("Location services connection failed with code " + connectionResult.getErrorCode());
}
}
#Override
public void onDestroy(){
mInProgress = false;
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
}
Here is the broadcast receiver:
public class MyLocationHandler extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
if (location != null && MyStatusHandler.getOptInStatus()) {
// debug messages
String msg = Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Log.d("debug", msg);
// log location to file
MyStatusHandler.logDataToFile("Location: "+msg);
// send Location to web service
MainActivity....(MyStatusHandler.getOptInStatus(), location, false);
}
if (location == null){
MyStatusHandler.logDataToFile("Location == NULL!");
}
}
}
I have set the interval to 5minutes and Fast interval to 2minutes.
NOTE: i removed some function calls and code for the web service operations
Everything works and i get updates as expected but at some point in time, the broadcast receiver doesn't get anything. When i check the logs, OptInStatus doesn't change and the last location broadcast i received was NULL.
The other parts of my code to call the web service and handle the status messages doesn't touch the service or location requests.
I'm trying to register two receivers, one that will receive messages from my app server through GCM and onother that will load messages from my server.
all this are in an activity called ChatActivity
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
d("Broadcast received FROM MMESSAGERECEIVER");
Toast.makeText(context, "mMessageReceiver started", Toast.LENGTH_LONG).show();
if(cust != null && adapter != null){
SharedPreferences sharedPref = ChatActivity.this.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
long userID = sharedPref.getLong(AllSystems.PREFERENCES_KEY_LOGGED_IN_USER_ID, -1);
// Extract data included in the Intent
String message = intent.getStringExtra("message");
String dateCreated = intent.getStringExtra("dateCreated");
Date d = new Date(Long.parseLong(dateCreated));
long senderId = Long.parseLong(intent.getStringExtra("senderId"));
Toast.makeText(context, "mMessageReceiver in the first if", Toast.LENGTH_LONG).show();
if(senderId == userID || senderId == cust.getId()){
Toast.makeText(context, "mMessageReceiver in the second if", Toast.LENGTH_LONG).show();
adapter.add(new ChatMessageData(senderId == cust.getId(), message, new DateTime(d)));
Bundle results = getResultExtras(true);
results.putBoolean(INTERCEPTED, true);
playSound();
}
}
}
};
private BroadcastReceiver mLoadedReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
d("Broadcast received");
d("Conversation loaded broadcast received");
if(task != null && cust != null){
d("Contact and task not null");
long contactId = intent.getLongExtra("contactId", -1);
if(contactId == cust.getId()){
d("Executing conversation loading task");
task.execute();
}
}
}
};
private void playSound(){
try {
Uri notification = Uri.parse("android.resource://com.me.myapp/" + R.raw.notif);
Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
r.play();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(mMessageReceiver, new IntentFilter("com.google.android.c2dm.intent.RECEIVE"));
LocalBroadcastManager.getInstance(this).registerReceiver(mLoadedReceiver, loadedFilter);
}
//Must unregister onPause()
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mMessageReceiver);
LocalBroadcastManager.getInstance(this).unregisterReceiver(mLoadedReceiver);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chats);
LocalBroadcastManager.getInstance(this).registerReceiver(mLoadedReceiver, loadedFilter);
registerReceiver(mMessageReceiver,new IntentFilter("com.google.android.c2dm.intent.RECEIVE"));
}
PROBLEM
the broadcast instance mMessageReceiver (the 1st line) isn't been registered since dialog(Toast) that are supposed to be activated in its onReceive method aren't been activated. this instance should receive GCM messages that why i have registed it like this ` registerReceiver(mMessageReceiver, new IntentFilter("com.google.android.c2dm.intent.RECEIVE"));
Question
Where am going wrong ? i have tried to follow the Try Cloud Messaging for Android and even the example at gitlab but all in vain. my previous question relation to this issue is here.
You need to declare a few permissions, services and GCMReceiver inside the manifest in order for GCM to work as intended.
Different page in the official documentation addresses GCM set up on an Android client in more depth. (refer here and sample here)
Hope this helped.