I'm trying to get the user's locations in real time using service. The problem happens when I try to do this below Android 8.0 because of the foreground service, I looked for several ways to do this but I didn't find any that work below Android 8.0.
Code that i used:
MainActivity
public class MainActivity extends AppCompatActivity {
#BindView(R.id.btn_start_tracking)
Button btnStartTracking;
#BindView(R.id.btn_stop_tracking)
Button btnStopTracking;
#BindView(R.id.txt_status)
TextView txtStatus;
public BackgroundService gpsService;
public boolean mTracking = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
final Intent intent = new Intent(this.getApplication(), BackgroundService.class);
this.getApplication().startService(intent);
this.getApplication().startForegroundService(intent);
this.getApplication().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
#OnClick(R.id.btn_start_tracking)
public void startLocationButtonClick() {
Dexter.withActivity(this)
.withPermission(Manifest.permission.ACCESS_FINE_LOCATION)
.withListener(new PermissionListener() {
#Override
public void onPermissionGranted(PermissionGrantedResponse response) {
gpsService.startTracking();
mTracking = true;
toggleButtons();
}
#Override
public void onPermissionDenied(PermissionDeniedResponse response) {
if (response.isPermanentlyDenied()) {
openSettings();
}
}
#Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
}
#OnClick(R.id.btn_stop_tracking)
public void stopLocationButtonClick() {
mTracking = false;
gpsService.stopTracking();
toggleButtons();
}
private void toggleButtons() {
btnStartTracking.setEnabled(!mTracking);
btnStopTracking.setEnabled(mTracking);
txtStatus.setText( (mTracking) ? "TRACKING" : "GPS Ready" );
}
private void openSettings() {
Intent intent = new Intent();
intent.setAction( Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
String name = className.getClassName();
if (name.endsWith("BackgroundService")) {
gpsService = ((BackgroundService.LocationServiceBinder) service).getService();
btnStartTracking.setEnabled(true);
txtStatus.setText("GPS Ready");
}
}
public void onServiceDisconnected(ComponentName className) {
if (className.getClassName().equals("BackgroundService")) {
gpsService = null;
}
}
};
}
Background service class
public class BackgroundService extends Service {
private final LocationServiceBinder binder = new LocationServiceBinder();
private final String TAG = "BackgroundService";
private LocationListener mLocationListener;
private LocationManager mLocationManager;
private NotificationManager notificationManager;
private final int LOCATION_INTERVAL = 500;
private final int LOCATION_DISTANCE = 10;
#Override
public IBinder onBind(Intent intent) {
return binder;
}
private class LocationListener implements android.location.LocationListener {
private Location lastLocation = null;
private final String TAG = "LocationListener";
private Location mLastLocation;
public LocationListener(String provider) {
mLastLocation = new Location(provider);
}
#Override
public void onLocationChanged(Location location) {
mLastLocation = location;
Log.i(TAG, "LocationChanged: "+location);
}
#Override
public void onProviderDisabled(String provider) {
Log.e(TAG, "onProviderDisabled: " + provider);
}
#Override
public void onProviderEnabled(String provider) {
Log.e(TAG, "onProviderEnabled: " + provider);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e(TAG, "onStatusChanged: " + status);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate");
startForeground(12345678, getNotification());
}
#Override
public void onDestroy() {
super.onDestroy();
if (mLocationManager != null) {
try {
mLocationManager.removeUpdates(mLocationListener);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listners, ignore", ex);
}
}
}
private void initializeLocationManager() {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
public void startTracking() {
initializeLocationManager();
mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER);
try {
mLocationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListener);
} catch (java.lang.SecurityException ex) {
// Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
// Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
}
public void stopTracking() {
this.onDestroy();
}
private Notification getNotification() {
NotificationChannel channel = new NotificationChannel("channel_01", "My Channel", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
Notification.Builder builder = new Notification.Builder(getApplicationContext(), "channel_01").setAutoCancel(true);
return builder.build();
}
public class LocationServiceBinder extends Binder {
public BackgroundService getService() {
return BackgroundService.this;
}
}
}
Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ac.myapp">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_GPS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<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=".maps"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:enabled="true"
android:name=".BootUpReceiver"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<service android:name=".BackgroundService"/>
</application>
</manifest>
Related
I have a simple application that collects locations. it works without for some minutes or hours but if I disconnect or loose Internet connection, or for disable/enable GPS 4-5 or more times, it not sends connection updates anymore. and I have to restart phone most times or kill that app and start it again (some times not works again after restart) to get locations again.
My application have a simple activity and a service for running in foreground and collection and sending them to server.
Here is my code:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.shetapp.ranandeg">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="mydefaultnotificationchannel" />
<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"
android:configChanges="orientation|screenSize"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".MyLocationTrackerService"
android:enabled="true"
android:exported="true" />
<service android:name=".MyFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
This is my activity :
public class MainActivity extends AppCompatActivity {
private final String TAG = "myapp-ma";
public static final String _TAG = "myapp-ma";
public static Context context;
private WebView webViewBrowser;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate: main activity created.");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivity.context = getApplicationContext();
loadPrefs(getApplicationContext());
getControles();
setBrowser();
startService(new Intent(this , MyLocationTrackerService.class));
}
#Override
protected void onDestroy() {
Log.d(TAG, "onDestroy: main activity destroyed.");
super.onDestroy();
}
#Override
protected void onResume() {
Log.d(TAG, "onResume: main activity resumed.");
super.onResume();
loadPrefs(getApplicationContext());
startService(new Intent(this , MyLocationTrackerService.class));
}
public void loadPrefs(Context context) {
Log.d(TAG, "loadPrefs: fired!");
SharedPreferences pref = getApplicationContext().getSharedPreferences(AppConfig.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
MyLocationTrackerService.USER_TOKEN = pref.getString(AppConfig.SHARED_PREFERENCES_USER_TOKEN , "xxx");
}
protected void getControles() {
Log.d(TAG, "getControles: fired!");
webViewBrowser = (WebView) findViewById(R.id.activity_main_webViewBrowser);
WebAppInterface.wv = webViewBrowser;
}
protected void setBrowser() {
webViewBrowser.addJavascriptInterface(new WebAppInterface(this), "NativeInterface");
WebSettings webSettings = webViewBrowser.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webViewBrowser.setWebViewClient(new MyWebViewClient());
webViewBrowser.setWebChromeClient(new MyWebChromeClient());
webViewBrowser.loadUrl("file:///android_asset/www/index.html");
}
#Override
public void onBackPressed() {
if(webViewBrowser.canGoBack() == true) {
webViewBrowser.goBack();
} else {
MainActivity.super.onBackPressed();
}
}
}
Location tracker service :
public class MyLocationTrackerService extends Service
{
private static final String TAG = "myapp-mlts";
private LocationManager mLocationManager = null;
private static final int LOCATION_INTERVAL = 1 * 60 * 1000; // MINUTES*60*1000
private static final float LOCATION_DISTANCE = 0f; // 100f;
public static String USER_TOKEN = "";
private static final int NOTIF_ID = 1;
private static final String NOTIF_CHANNEL_ID = "Channel_Id";
private BroadcastReceiver mRegistrationBroadcastReceiver;
private class LocationListener implements android.location.LocationListener
{
Location mLastLocation;
public LocationListener(String provider)
{
mLastLocation = new Location(provider);
}
#Override
public void onLocationChanged(Location location)
{
Log.d(TAG, "onLocationChanged: location change detected , "+location.getProvider());
mLastLocation.set(location);
updateServerLocation(getApplication() , location , AppConfig.SERVER_IP , MyLocationTrackerService.USER_TOKEN);
}
#Override
public void onProviderDisabled(String provider)
{
Log.d(TAG, "LocationListener onProviderDisabled , "+provider);
}
#Override
public void onProviderEnabled(String provider)
{
Log.d(TAG, "LocationListener onProviderEnabled , "+provider);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
Log.d(TAG, "LocationListener onStatusChanged , " + provider);
}
protected void updateServerLocation(Context context, final Location location , final String ip , final String userToken)
{
Log.d(TAG, "LocationListener updateServerLocation, token/server : "+userToken+"/"+ip);
if(ip.equals("")){
Log.d(TAG , "ip of server is empty string. returned.");
} else if(!isConnected()){
Log.d(TAG, "updateServerLocation: not connected!");
} else if(userToken.length() < 1){
Log.d(TAG, "updateServerLocation: userToken.length() < 1 , userToken is : "+userToken);
} else {
String url = ip + "/app/ranande/location";
StringRequest request = new StringRequest(
Request.Method.POST,
url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try{
Log.d(TAG, "onResponse: " + response);
} catch (Exception e){
Log.d(TAG, "onResponse: " + e.getMessage().toString());
}
}
},
new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "onErrorResponse: " + error.toString());
}
}
){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
String token = userToken;
if( token.equals(null) || token.equals("") ){
Log.d(TAG , "token is empty string, returned.");
}
params.put("token", token);
params.put("latitude", String.valueOf(location.getLatitude()));
params.put("longitude", String.valueOf(location.getLongitude()));
return params;
}
};
VolleyController.getInstance(context).addToRequestQueue(request);
}
}
}
LocationListener[] mLocationListeners = new LocationListener[] {
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.NETWORK_PROVIDER)
};
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
Log.d(TAG, "MyLocationTrackerService onStartCommand fired.");
startForeground();
try{
Log.d(TAG, "MyLocationTrackerService onStartCommand: token/ip are "+MyLocationTrackerService.USER_TOKEN+"/"+AppConfig.SERVER_IP);
} catch (Exception ex){
Log.d(TAG, "onStartCommand: Exception! : "+ex.toString());
}
try{
super.onStartCommand(intent, flags, startId);
} catch (Exception ex){
Log.e(TAG, "onStartCommand: Exception on calling super.onStartCommand," +ex.toString());
}
return START_STICKY;
}
#Override
public void onCreate()
{
Log.d(TAG, "MyLocationTrackerService created.");
initializeLocationManager();
try {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
//use checkSelfPermission()
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
mLocationListeners[1]);
} else {
//simply use the required feature
//as the user has already granted permission to them during installation
mLocationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
mLocationListeners[1]);
}
} catch (java.lang.SecurityException ex) {
Log.d(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "network provider does not exist, " + ex.getMessage());
}
try {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, LOCATION_INTERVAL, LOCATION_DISTANCE,
mLocationListeners[0]);
} catch (java.lang.SecurityException ex) {
Log.d(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
}
#Override
public void onDestroy()
{
Log.d(TAG, "MyLocationTrackerService destroyed.");
super.onDestroy();
if (mLocationManager != null) {
for (int i = 0; i < mLocationListeners.length; i++) {
try {
mLocationManager.removeUpdates(mLocationListeners[i]);
} catch (Exception ex) {
Log.d(TAG, "fail to remove location listners, ignore", ex);
}
}
}
}
private void initializeLocationManager() {
try{
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
} catch (Exception ex) {
Log.d(TAG, "initializeLocationManager: error : "+ex.toString());
}
}
public Boolean isConnected(){
ConnectivityManager connectivityManager = (ConnectivityManager)getApplicationContext().getSystemService(MainActivity.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if(networkInfo != null && networkInfo.isConnected()){
return true;
}
return false;
}
private void startForeground() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
startForeground(NOTIF_ID, new NotificationCompat.Builder(this,
NOTIF_CHANNEL_ID) // don't forget create a notification channel first
.setOngoing(true)
.setContentTitle(getString(R.string.app_name))
.setContentText("running ...")
.setContentIntent(pendingIntent)
.setSmallIcon(R.mipmap.globe)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round))
.build());
}
}
After restart phone, my application works without any issue, some times I have to kill the Application from application manager and run it again but some times I have to restart the phone.
I think maybe there is an bug with phones GPS or android OS not mine App.
IS THERE ANY ERROR IN MY CODE? or how I could change and solve this problem? Is it good idea to use fuse/google service instead?
I have a connection with SIP server(Asterisk), but micro and dynamics doesn't work. Asterisk kicks client in 31 seconds(Empty RTP thread). Documentation by Google says:
Android provides an API that supports the Session Initiation Protocol (SIP). This lets you add SIP-based internet telephony features to your applications. Android includes a full SIP protocol stack and integrated call management services that let applications easily set up outgoing and incoming voice calls, without having to manage sessions, transport-level communication, or audio record or playback directly.
Activity + Receiver:
public class MainActivity extends AppCompatActivity {
public String domain = "192.168.10.37";
public String name = "111";
public String password = "123456";
public String sipAddress = "100#192.168.10.37";
public IncomingCallReceiver receiver;
public SipManager sipManager;
public SipProfile sipProfile;
public SipAudioCall call;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.USE_SIP)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.USE_SIP)) {
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.USE_SIP},
0);
}
}
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("zlotnikov.SIPexample.INCOMING_CALL");
receiver = new IncomingCallReceiver();
this.registerReceiver(receiver, intentFilter);
initManager();
}
private void initManager() {
if (sipManager == null) {
sipManager = SipManager.newInstance(this);
}
}
private void initializeLocalProfile() {
if (sipProfile != null) {
closeLocalProfile();
}
try {
SipProfile.Builder builder = new SipProfile.Builder(name, domain);
builder.setPassword(password);
builder.setSendKeepAlive(true);
builder.setAutoRegistration(true);
sipProfile = builder.build();
Intent intent = new Intent();
intent.setAction("zlotnikov.SIPexample.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
SipRegistrationListener listener = new SipRegistrationListener() {
#Override
public void onRegistering(String s) {
System.out.println("voip onRegistering " + s);
}
#Override
public void onRegistrationDone(String s, long l) {
System.out.println("voip onRegistrationDone " + s + " " + l);
//initCall();
}
#Override
public void onRegistrationFailed(String s, int i, String s1) {
System.out.println("voip onRegistrationFailed " + s);
}
};
sipManager.open(sipProfile, pendingIntent, null);
//sipManager.register(sipProfile, 40, listener);
sipManager.setRegistrationListener(sipProfile.getUriString(), listener);
} catch (ParseException e) {
e.printStackTrace();
} catch (SipException e) {
e.printStackTrace();
}
}
#Override
protected void onStart() {
super.onStart();
initializeLocalProfile();
}
#Override
protected void onStop() {
super.onStop();
closeLocalProfile();
}
private void closeLocalProfile() {
try {
if (sipProfile != null) {
sipManager.close(sipProfile.getUriString());
}
} catch (Exception ee) {
ee.printStackTrace();
}
}
private void initCall() {
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onCallEstablished(SipAudioCall call) {
super.onCallEstablished(call);
System.out.println("voip onCallEstablished");
call.startAudio();
call.setSpeakerMode(true);
}
#Override
public void onCallEnded(SipAudioCall call) {
super.onCallEnded(call);
System.out.println("voip onCallEnded");
}
};
call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);
} catch (SipException e) {
closeLocalProfile();
call.close();
e.printStackTrace();
System.out.println("voip MainActivity Конец соединения");
}
}
public class IncomingCallReceiver extends BroadcastReceiver {
private MediaPlayer mediaPlayer;
#Override
public void onReceive(Context context, Intent intent) {
SipAudioCall incomingCall = null;
System.out.println("voip Пришел звонок " + intent.toString());
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onRinging(SipAudioCall call, SipProfile caller) {
System.out.println("voip onRinging()");
try {
startRinging();
} catch (Exception e) {
stopRinging();
System.out.println("voip onRinging exception");
e.printStackTrace();
}
}
#Override
public void onCallEstablished(SipAudioCall call) {
super.onCallEstablished(call);
System.out.println("voip onCallEstablished()");
stopRinging();
}
#Override
public void onCallEnded(SipAudioCall call) {
super.onCallEnded(call);
System.out.println("voip onCallEnded()");
}
};
incomingCall = sipManager.takeAudioCall(intent, listener);
incomingCall.startAudio();
incomingCall.setSpeakerMode(true);
/*if (incomingCall.isMuted()) {
incomingCall.toggleMute();
}*/
//call = incomingCall;
incomingCall.answerCall(30);
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
System.out.println("voip IncomingCallReceiver конец соединения");
}
}
}
private synchronized void startRinging() {
long[] pattern = {0, 1000, 1000};
((Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE)).vibrate(pattern, 0);
Uri defaultRingtoneUri = RingtoneManager.getActualDefaultRingtoneUri(getApplicationContext(), RingtoneManager.TYPE_RINGTONE);
mediaPlayer = MediaPlayer.create(getApplicationContext(), defaultRingtoneUri);
mediaPlayer.setLooping(true);
mediaPlayer.start();
}
private synchronized void stopRinging() {
((Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE))
.cancel();
if (mediaPlayer != null || mediaPlayer.isPlaying()) mediaPlayer.stop();
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="zlotnikov.sipexample">
<uses-permission android:name="android.permission.USE_SIP" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.sip.voip" />
<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"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".MainActivity$IncomingCallReceiver" android:label="Call Receiver" />
</application>
I am trying to send a message from my mobile app to a service in the wearable app.
The message needs to be send from a BroadcastReceiver. In the BroadcastReceiver the message is sent successfully, at least that is what is said in the log, but the method onMessageReceive is not triggered.
I am not quite sure what the problem might be, I have the same applicationId in both build.gradle files, but packages names are different.
In mobile manifest the receiver has the following declaration:
<receiver android:name="com.app.mobile.NewAlarm"
android:exported="false">
<intent-filter>
<action android:name="com.example.android.support.wearable.notifications.ACTION_EXAMPLE" />
</intent-filter>
</receiver>
the BroadcastReceiver class:
public class TimeAlarm extends BroadcastReceiver {
public static final String TAP_ACTION_PATH = "/tap";
private static final String TAG = "NotificationReceiver";
public void onReceive(Context context, Intent paramIntent) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
}
#Override
public void onConnectionSuspended(int cause) {
}
}).build();
mGoogleApiClient.connect();
sendMessage();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
private void getNodes() {
Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(
new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult getConnectedNodesResult) {
HashSet<String> results = new HashSet<String>();
for (Node node : getConnectedNodesResult.getNodes()) {
results.add(node.getId());
Log.d(TAG,node.getId().toString());
}
Log.d(TAG,results.toString());
sendMessageApi(results);
}
}
);
}
private void sendMessageApi(Collection<String> nodes) {
for (String node : nodes) {
Wearable.MessageApi.sendMessage(
mGoogleApiClient, node, TAP_ACTION_PATH, null).setResultCallback(
new ResultCallback<MessageApi.SendMessageResult>() {
#Override
public void onResult(MessageApi.SendMessageResult sendMessageResult) {
if (!sendMessageResult.getStatus().isSuccess()) {
Log.e(TAG, "ERROR: failed to send Message: " + sendMessageResult.getStatus());
} else {
Log.e(TAG, "Success!");
}
}
}
);
}
}
private void sendMessage() {
getNodes();
}
}
The WearableListenerService is declared this way in wear app manifest:
<service android:name=".DataLayerListenerService" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.BIND_LISTENER" />
</intent-filter>
</service>
and finally the DataLayerListenerService:
public class DataLayerListenerService extends WearableListenerService {
public static final String START_ACTIVITY_PATH = "/notice";
public static final String TAP_ACTION_PATH = "/tap";
private static final String TAG = "DataLayerListenerService";
#Override
public void onMessageReceived(MessageEvent messageEvent) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
Log.d(TAG, "Message Received !!");
if (START_ACTIVITY_PATH.equals(messageEvent.getPath())) {
Log.d(TAG, "Message Received !!");
int vibeTime = 5000;
String strVibeTime = new String(messageEvent.getData());
if (!strVibeTime.equals("") && !strVibeTime.equals("0")) {
vibeTime = (Integer.parseInt(strVibeTime)) * 1000;
}
sleep(1000);
vibrator.vibrate(vibeTime);
} else if (TAP_ACTION_PATH.equals(messageEvent.getPath())) {
Log.d(TAG, "Tapping Received !!");
vibrator.cancel();
}
}
public synchronized void sleep(long msec) {
try
{
wait(msec);
}catch(InterruptedException e){
Log.e(TAG, e.getMessage());
}
}
}
You're trying to send the message before the client is connected, so getConnectedNodesResult.getNodes() is returning an empty list.
Try this instead:
public void onReceive(Context context, Intent paramIntent) {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(Wearable.API)
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(Bundle bundle) {
sendMessage();
}
#Override
public void onConnectionSuspended(int cause) {
}
}).build();
mGoogleApiClient.connect();
}
I use estimote android sdk for becan detaction or scan but i did not get beacons and there list call. i setup my code according to Readme file shows on lib and complie code but not get becons.
https://github.com/Estimote/Android-SDK
private static final String ESTIMOTE_PROXIMITY_UUID = "B9407F30-F5F8-466E-AFF9-25556B57FE6D";
private static final Region ALL_ESTIMOTE_BEACONS = new Region("regionId", ESTIMOTE_PROXIMITY_UUID, null, null);
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_signup);
beaconManager = new BeaconManager(SignupActivity.this);
// Should be invoked in #onCreate.
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override
public void onBeaconsDiscovered(Region arg0, List<Beacon> beacons) {
Log.d("TAG", "Ranged beacons: " + beacons);
}
});
}
#Override
protected void onStart() {
super.onStart();
// Should be invoked in #onStart.
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
#Override
public void onServiceReady() {
try {
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS);
} catch (RemoteException e) {
Log.e("TAG", "Cannot start ranging", e);
}
}
});
}
#Override
protected void onStop() {
super.onStop();
try {
beaconManager.stopRanging(ALL_ESTIMOTE_BEACONS);
} catch (RemoteException e) {
Log.e("TAG", "Cannot stop but it does not matter now", e);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
beaconManager.disconnect();
}
Change
private static final Region ALL_ESTIMOTE_BEACONS = new Region("regionId", ESTIMOTE_PROXIMITY_UUID, null, null);
to
private static final Region ALL_ESTIMOTE_BEACONS = new Region("regionId", null, null, null);
And also Change
beaconManager = new BeaconManager(SignupActivity.this);
// Should be invoked in #onCreate.
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override
public void onBeaconsDiscovered(Region arg0, List<Beacon> beacons) {
Log.d("TAG", "Ranged beacons: " + beacons);
}
});
to
beaconManager = new BeaconManager(SignupActivity.this);
// Should be invoked in #onCreate.
beaconManager.setRangingListener(new BeaconManager.RangingListener() {
#Override
public void onBeaconsDiscovered(Region arg0, List<Beacon> beacons) {
Log.d("TAG", "Ranged beacons: " + beacons.size());
}
});
Here you can see the size of near by beacons
Download jar file.
https://github.com/Estimote/Android-SDK
https://github.com/Estimote/Android-SDK/blob/master/EstimoteSDK/estimote-sdk-preview.jar
Mainifeast code.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#drawable/app_icon"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.user.TempActivity"
android:configChanges="orientation|screenSize"
android:label="#string/app_name"
android:windowSoftInputMode="stateHidden|adjustPan" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="com.estimote.sdk.service.BeaconService"
android:exported="false" >
</service>
</application>
</manifest>
// Example activity
public class TempActivity extends Activity {
private static final String ESTIMOTE_PROXIMITY_UUID = "B9407F30-F5F8-466E-AFF9-25556B57FE6D";
private static final Region ALL_ESTIMOTE_BEACONS = new Region("regionId", ESTIMOTE_PROXIMITY_UUID, null, null);
private BeaconManager beaconManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.temp_activity);
startBeaconDetectionService();
WebAsynk mWebAsynk= new WebAsynk();
mWebAsynk.execute(BEACON_WEB_SERVICE);
}
#Override
protected void onStart() {
super.onStart();
// Should be invoked in #onStart.
beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
#Override
public void onServiceReady() {
try {
beaconManager.startRanging(ALL_ESTIMOTE_BEACONS);
} catch (RemoteException e) {
Log.e("TAG", "Cannot start ranging", e);
}
}
});
}
#Override
protected void onStop() {
super.onStop();
try {
beaconManager.stopRanging(ALL_ESTIMOTE_BEACONS);
} catch (RemoteException e) {
Log.e("TAG", "Cannot stop but it does not matter now", e);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
beaconManager.disconnect();
}
/**
* Beacon Detection Service response handle here
*/
private void startBeaconDetectionService(){
beaconManager = new com.estimote.sdk.BeaconManager(TempActivity.this);
beaconManager.setForegroundScanPeriod(2000,2000);
// Should be invoked in #onCreate.
beaconManager.setRangingListener(new com.estimote.sdk.BeaconManager.RangingListener() {
#Override
public void onBeaconsDiscovered(Region arg0, final List<Beacon> beacons) {
Log.d("TAG", "Ranged beacons: " + beacons);
Log.e("TAG", "Ranged beacons: " + beacons);
runOnUiThread(new Runnable() {
#Override
public void run() {
int test=0;
if(beacons.size()<=0){
Toast.makeText(TempActivity.this, "No beacon found",
Toast.LENGTH_SHORT).show();
}else{
for (int i = 0; i < beacons.size(); i++) {
String beac=beacons.get(i).getProximityUUID();
String macAddress=beacons.get(i).getMacAddress();
int major=beacons.get(i).getMajor();
int minor=beacons.get(i).getMinor();
int rssi=beacons.get(i).getRssi();
// Filter for right beacon detection
Toast.makeText(TempActivity.this, "I found a beacon with UUID; "+beac+" M-"+major,
Toast.LENGTH_SHORT).show();
}
}
}
});
}
});
}
}
I already have this application that once the user clicks a button, the main activity calls a Broadcast Receiver that in turn calls a Service which sends the GPS Location of the user to an EMail Address in a certain interval. The app works just fine.
However, what I want to do is that when the user activates the Power Button of the device, the Broadcast Receiver will activate. So far, I've seen this question and I've used it so my manifest file looks something like:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.angelo.serviceexample"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.angelo.serviceexample.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.permission.PREVENT_POWER_KEY" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="com.angelo.serviceexample.GPSLoggerService" />
<receiver android:name="com.angelo.serviceexample.AlarmReceiver">
<intent-filter>
<action android:name="android.intent.action.SCREEN_OFF"></action>
<action android:name="android.intent.action.SCREEN_ON"></action>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"> </action>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"></action>
<action android:name="android.intent.action.ACTION_SHUTDOWN"></action>
</intent-filter>
</receiver>
</application>
</manifest>
And the Broadcast Receiver class has intent-filters to call it when activated.
The AlarmReceiver.java file looks like this so far:
package com.angelo.serviceexample;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context,GPSLoggerService.class));
//Toast.makeText(, "power button clicked",Toast.LENGTH_LONG).show();
Log.v("Tag", "AlarmReceiver called.");
}
}
However, when I was checking LogCat, the Log only activates whenever the user presses the button in the Main Activity as shown here:
public class MainActivity extends Activity {
private int currentIntervalChoice = 0;
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) {
setAppInfo();
addButtonListeners();
enableControls();
return true;
}
return super.dispatchKeyEvent(event);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAppInfo();
addButtonListeners();
enableControls();
}
private void setAppInfo() {
TextView txtInfo = (TextView)findViewById(R.id.app_info);
txtInfo.setText(Html.fromHtml(getString(R.string.app_info)));
Linkify.addLinks(txtInfo, Linkify.ALL);
}
private void addButtonListeners() {
((Button)findViewById(R.id.start_logging)).setOnClickListener(btnClick);
((Button)findViewById(R.id.logging_interval)).setOnClickListener(btnClick);
}
private void enableControls(){
boolean isServiceRunning = AppSettings.getServiceRunning(this);
String buttonText = getString(R.string.start_logging);
if(isServiceRunning){
buttonText = getString(R.string.stop_logging);
((Button)findViewById(R.id.logging_interval)).setEnabled(false);
}
else{
((Button)findViewById(R.id.logging_interval)).setEnabled(true);
}
((Button)findViewById(R.id.start_logging)).setText(buttonText);
}
private void changeLoggingIntercal(){
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
final String loggingIntervals[] = { "1 minute", "3 minute", "5 minutes", "10 minutes" };
builder.setTitle(getString(R.string.logging_interval));
builder.setSingleChoiceItems(loggingIntervals, currentIntervalChoice, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
currentIntervalChoice = which;
setLoggingInterval(currentIntervalChoice);
dialog.dismiss();
}
});
builder.show();
}
private void setLoggingInterval(int intervalChoice){
int interval = 1;
switch(intervalChoice){
case 0: interval = 1; break;
case 1: interval = 3; break;
case 2: interval = 5; break;
case 3: interval = 10; break;
default: interval = 1; break;
}
AppSettings.setLoggingInterval(this, interval);
}
public void setLogFileName(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateString = sdf.format(new Date());
String filename = "GPSLog." + dateString + ".kml";
AppSettings.setLogFileName(this, filename);
}
private View.OnClickListener btnClick = new View.OnClickListener() {
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.start_logging:{
toggleLogging(AppSettings.getServiceRunning(MainActivity.this),
AppSettings.getLoggingInterval(MainActivity.this));
enableControls();
break;
}
case R.id.logging_interval:{
changeLoggingIntercal();
break;
}
}
}
};
private void toggleLogging(boolean isStart, int interval){
AlarmManager manager = (AlarmManager)getSystemService(Service.ALARM_SERVICE);
PendingIntent loggerIntent = PendingIntent.getBroadcast(this, 0,new Intent(this,AlarmReceiver.class), 0);
if(isStart){
manager.cancel(loggerIntent);
AppSettings.setServiceRunning(this, false);
AppLog.logString("Service Stopped.");
}
else{
setLogFileName();
long duration = interval * 60 * 1000;
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), duration, loggerIntent);
AppSettings.setServiceRunning(this, true);
Toast.makeText(getApplicationContext(), "Service Started with interval " + interval
+ ", Logfile name: " + AppSettings.getLogFileName(this), Toast.LENGTH_LONG).show();
AppLog.logString("Service Started with interval " + interval
+ ", Logfile name: " + AppSettings.getLogFileName(this));
}
}
}
Also, this is the Service Class that is in question:
public class GPSLoggerService extends Service implements LocationListener{
private static final int gpsMinTime = 500;
private static final int gpsMinDistance = 0;
private static final int TIMER_DELAY = 1000;
private LocationManager manager = null;
private double latitude = 0.0;
private double longitude = 0.0;
private Timer monitoringTimer = null;
public GPSLoggerService() {
AppLog.logString("GPSLoggerService.GPSLoggerService().");
}
#Override
public IBinder onBind(Intent arg0) {
AppLog.logString("GPSLoggerService.onBind().");
return null;
}
#Override
public void onCreate() {
AppLog.logString("GPSLoggerService.onCreate().");
super.onCreate();
}
public int onStartCommand(Intent intent, int flags, int startId) {
AppLog.logString("GPSLoggerService.onStartCommand().");
startLoggingService();
startMonitoringTimer();
return Service.START_STICKY;
}
#Override
public void onLocationChanged(Location location) {
AppLog.logString("GPSLoggerService.onLocationChanged().");
latitude = location.getLatitude();
longitude = location.getLongitude();
}
#Override
public void onProviderDisabled(String provider) {
AppLog.logString("GPSLoggerService.onProviderDisabled().");
}
#Override
public void onProviderEnabled(String provider) {
AppLog.logString("GPSLoggerService.onProviderEnabled().");
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
AppLog.logString("GPSLoggerService.onStatusChanged().");
}
private void startLoggingService(){
if (manager == null){
manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
final Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW);
final String bestProvider = manager.getBestProvider(criteria, true);
if (bestProvider != null && bestProvider.length() > 0){
manager.requestLocationUpdates(bestProvider, gpsMinTime,gpsMinDistance, this);
}
else{
final List<String> providers = manager.getProviders(true);
for (final String provider : providers){
manager.requestLocationUpdates(provider, gpsMinTime, gpsMinDistance, this);
}
}
}
private void stopLoggingService(){
stopSelf();
}
private void startMonitoringTimer(){
monitoringTimer = new Timer();
monitoringTimer.scheduleAtFixedRate(
new TimerTask(){
#Override
public void run(){
if (longitude != 0.0 && latitude != 0.0){
monitoringTimer.cancel();
monitoringTimer = null;
manager.removeUpdates(GPSLoggerService.this);
sendCoordinates(latitude, longitude);
stopLoggingService();
}
}
},
GPSLoggerService.TIMER_DELAY,
GPSLoggerService.TIMER_DELAY);
}
private void sendCoordinates(double latitude, double longitude){
Looper.prepare();
GMailSender sender = new GMailSender("sender#gmail.com", "password");
//Subject, Body, Sender, Recipient
try {
sender.sendMail("Sample GPS Location",
"This should go to my yahoo account. My Location is at is - Lat: " + latitude + " Long: " + longitude,
"sender#gmail.com",
"receiver#yahoo.com");
Toast.makeText(getApplicationContext(), "Mail Sent", Toast.LENGTH_SHORT).show();
}
catch (Exception e) {
Log.e("SendMail", e.getMessage(), e);
}
}
}
So far, I'm reading online on how to call a Broadcast Receiver through a phone event which in turn, calls the Service. I think I'm having problems because of the bind override I set though I'm not sure.
u have to start the services
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service=new Intent(context,UploadObservationService.class);
**context.startService(service);**
Log.v("Tag", "AlarmReceiver called.");
}
}