1 package org.opencpn; 2 3 import android.app.AlertDialog; 4 import android.app.Service; 5 import android.content.Context; 6 import android.content.DialogInterface; 7 import android.content.Intent; 8 import android.content.pm.PackageManager; 9 import android.location.Location; 10 import android.location.LocationListener; 11 import android.location.LocationManager; 12 import android.location.GpsStatus; 13 import android.location.GpsSatellite; 14 15 import android.os.Bundle; 16 import android.os.IBinder; 17 import android.os.HandlerThread; 18 import android.os.SystemClock; 19 import android.provider.Settings; 20 import android.util.Log; 21 import android.app.Activity; 22 import android.os.Handler; 23 import java.util.List; 24 import java.lang.Math; 25 import java.lang.Iterable; 26 import java.util.Iterator; 27 28 import org.opencpn.OCPNGpsNmeaListener; 29 import org.opencpn.OCPNNativeLib; 30 import org.qtproject.qt5.android.bindings.QtActivity; 31 32 33 34 public class GPSServer extends Service implements LocationListener { 35 36 private final static int GPS_OFF = 0; 37 private final static int GPS_ON = 1; 38 public final static int GPS_PROVIDER_AVAILABLE = 2; 39 private final static int GPS_SHOWPREFERENCES = 3; 40 41 private final Context mContext; 42 private final Activity parent_activity; 43 44 public String status_string; 45 46 boolean isThreadStarted = false; 47 HandlerThread mLocationHandlerThread; 48 49 OCPNGpsNmeaListener mNMEAListener; 50 OCPNNativeLib mNativeLib; 51 52 // flag for GPS status 53 boolean isGPSEnabled = false; 54 55 // flag for network status 56 boolean isNetworkEnabled = false; 57 58 // flag for GPS status 59 boolean canGetLocation = false; 60 61 Location mLastLocation; // location 62 double latitude; // latitude 63 double longitude; // longitude 64 float course; 65 float speed; 66 67 private GpsStatus mStatus; 68 private MyListener mMyListener; 69 long mLastLocationMillis; 70 boolean isGPSFix = false; 71 public int m_watchDog = 0; 72 73 int m_tick; 74 75 // The minimum distance to change Updates in meters 76 private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 1; // 1 meter 77 78 // The minimum time between updates in milliseconds 79 private static final long MIN_TIME_BW_UPDATES = 1000; // 1 second 80 81 // Declaring a Location Manager 82 protected LocationManager locationManager; 83 84 private class MyListener implements GpsStatus.Listener { 85 @Override onGpsStatusChanged(int event)86 public void onGpsStatusChanged(int event) { 87 // Log.i("DEBUGGER_TAG", "StatusListener Event"); 88 89 if(null != locationManager){ 90 mStatus = locationManager.getGpsStatus(mStatus); 91 } 92 93 94 switch (event) { 95 case GpsStatus.GPS_EVENT_STARTED: 96 Log.i("DEBUGGER_TAG", "GPS_EVENT_STARTED Event"); 97 break; 98 99 case GpsStatus.GPS_EVENT_STOPPED: 100 Log.i("DEBUGGER_TAG", "GPS_EVENT_STOPPED Event"); 101 isGPSFix = false; 102 break; 103 104 case GpsStatus.GPS_EVENT_FIRST_FIX: 105 Log.i("DEBUGGER_TAG", "GPS_EVENT_FIRST_FIX Event"); 106 isGPSFix = true; 107 break; 108 109 case GpsStatus.GPS_EVENT_SATELLITE_STATUS: 110 // Log.i("DEBUGGER_TAG", "GPS_EVENT_SATELLITE_STATUS Event"); 111 112 int nSatsUsed = 0; 113 // int maxSatellites = gpsStatus.getMaxSatellites(); // appears fixed at 255 114 Iterable<GpsSatellite>satellites = mStatus.getSatellites(); 115 Iterator<GpsSatellite>satI = satellites.iterator(); 116 while (satI.hasNext()) { 117 GpsSatellite satellite = satI.next(); 118 // Log.i("DEBUGGER_TAG", "onGpsStatusChanged(): " + satellite.getPrn() + "," + satellite.usedInFix() + "," + satellite.getSnr() + "," + satellite.getAzimuth() + "," + satellite.getElevation()); 119 if(satellite.usedInFix()) 120 nSatsUsed++; 121 } 122 123 if(nSatsUsed < 3) 124 isGPSFix = false; 125 126 break; 127 } 128 } 129 } 130 GPSServer(Context context, OCPNNativeLib nativelib, Activity activity)131 public GPSServer(Context context, OCPNNativeLib nativelib, Activity activity) { 132 this.mContext = context; 133 this.mNativeLib = nativelib; 134 this.parent_activity = activity; 135 // getLocation(); 136 } 137 doService( int parm )138 public String doService( int parm ) 139 { 140 String ret_string = "???"; 141 locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE); 142 143 switch (parm){ 144 case GPS_OFF: 145 Log.i("DEBUGGER_TAG", "GPS OFF"); 146 147 if(locationManager != null){ 148 if(isThreadStarted){ 149 locationManager.removeUpdates(GPSServer.this); 150 locationManager.removeNmeaListener (mNMEAListener); 151 isThreadStarted = false; 152 } 153 } 154 155 ret_string = "GPS_OFF OK"; 156 break; 157 158 case GPS_ON: 159 Log.i("DEBUGGER_TAG", "GPS ON"); 160 161 isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); 162 163 if(isGPSEnabled){ 164 Log.i("DEBUGGER_TAG", "GPS is Enabled"); 165 } 166 else{ 167 Log.i("DEBUGGER_TAG", "GPS is <<<<DISABLED>>>>"); 168 ret_string = "GPS is disabled"; 169 status_string = ret_string; 170 return ret_string; 171 } 172 173 /* 174 isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); 175 if(isNetworkEnabled) 176 Log.i("DEBUGGER_TAG", "Network is Enabled"); 177 else 178 Log.i("DEBUGGER_TAG", "Network is <<<<DISABLED>>>>"); 179 */ 180 181 if(!isThreadStarted){ 182 183 parent_activity.runOnUiThread(new Runnable() { 184 LocationManager locationManager; 185 public void run() { 186 187 locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE); 188 Log.i("DEBUGGER_TAG", "Requesting Updates"); 189 locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000,1, GPSServer.this); 190 191 mNMEAListener = new OCPNGpsNmeaListener(mNativeLib, GPSServer.this); 192 locationManager.addNmeaListener (mNMEAListener); 193 194 mMyListener = new MyListener(); 195 locationManager.addGpsStatusListener(mMyListener); 196 197 } 198 }); 199 200 201 HandlerThread hThread = new HandlerThread("HandlerThread"); 202 hThread.start(); 203 final Handler handler = new Handler(hThread.getLooper()); 204 205 206 Runnable ticker = new Runnable() { 207 @Override 208 public void run() { 209 // Log.i("DEBUGGER_TAG", "Tick"); 210 211 m_tick++; 212 m_watchDog++; 213 214 if(isGPSEnabled && (m_watchDog > 10)){ 215 if(null != locationManager){ 216 mLastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); 217 if (mLastLocation != null) { 218 latitude = mLastLocation.getLatitude(); 219 longitude = mLastLocation.getLongitude(); 220 course = mLastLocation.getBearing(); 221 speed = mLastLocation.getSpeed(); 222 } 223 224 if(null != mNativeLib){ 225 String s = createRMC(); 226 mNativeLib.processNMEA( s ); 227 } 228 } 229 } 230 231 handler.postDelayed(this, 1000); 232 } 233 }; 234 235 // Schedule the first execution 236 handler.postDelayed(ticker, 1000); 237 238 239 isThreadStarted = true; 240 } 241 242 ret_string = "GPS_ON OK"; 243 break; 244 245 case GPS_PROVIDER_AVAILABLE: 246 if(hasGPSDevice( mContext )){ 247 ret_string = "YES"; 248 Log.i("DEBUGGER_TAG", "Provider yes"); 249 } 250 else{ 251 ret_string = "NO"; 252 Log.i("DEBUGGER_TAG", "Provider no"); 253 } 254 255 break; 256 257 case GPS_SHOWPREFERENCES: 258 showSettingsAlert(); 259 break; 260 261 } // switch 262 263 264 status_string = ret_string; 265 return ret_string; 266 } 267 268 269 hasGPSDevice(Context context)270 public boolean hasGPSDevice(Context context) 271 { 272 273 // This code crashes unless run from the GUI thread, so is moved to the QtActivity initialization 274 // PackageManager packMan = getPackageManager(); 275 // return packMan.hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); 276 277 // This code produces false positive for some generic android tablets. 278 final LocationManager mgr = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); 279 if ( mgr == null ) 280 return false; 281 final List<String> providers = mgr.getAllProviders(); 282 if ( providers == null ) 283 return false; 284 return providers.contains(LocationManager.GPS_PROVIDER); 285 286 } 287 288 getLocation()289 public Location getLocation() { 290 try { 291 locationManager = (LocationManager) mContext 292 .getSystemService(LOCATION_SERVICE); 293 294 // getting GPS status 295 isGPSEnabled = locationManager 296 .isProviderEnabled(LocationManager.GPS_PROVIDER); 297 298 // getting network status 299 isNetworkEnabled = locationManager 300 .isProviderEnabled(LocationManager.NETWORK_PROVIDER); 301 302 if (!isGPSEnabled && !isNetworkEnabled) { 303 // no network provider is enabled 304 } else { 305 this.canGetLocation = true; 306 // First get location from Network Provider 307 if (isNetworkEnabled) { 308 locationManager.requestLocationUpdates( 309 LocationManager.NETWORK_PROVIDER, 310 MIN_TIME_BW_UPDATES, 311 MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 312 Log.d("Network", "Network"); 313 if (locationManager != null) { 314 mLastLocation = locationManager 315 .getLastKnownLocation(LocationManager.NETWORK_PROVIDER); 316 if (mLastLocation != null) { 317 latitude = mLastLocation.getLatitude(); 318 longitude = mLastLocation.getLongitude(); 319 } 320 } 321 } 322 // if GPS Enabled get lat/long using GPS Services 323 if (isGPSEnabled) { 324 if (mLastLocation == null) { 325 locationManager.requestLocationUpdates( 326 LocationManager.GPS_PROVIDER, 327 MIN_TIME_BW_UPDATES, 328 MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 329 Log.d("GPS Enabled", "GPS Enabled"); 330 if (locationManager != null) { 331 mLastLocation = locationManager 332 .getLastKnownLocation(LocationManager.GPS_PROVIDER); 333 if (mLastLocation != null) { 334 latitude = mLastLocation.getLatitude(); 335 longitude = mLastLocation.getLongitude(); 336 } 337 } 338 } 339 } 340 } 341 342 } catch (Exception e) { 343 e.printStackTrace(); 344 } 345 346 return mLastLocation; 347 } 348 349 /** 350 * Stop using GPS listener 351 * Calling this function will stop using GPS in your app 352 * */ stopUsingGPS()353 public void stopUsingGPS(){ 354 if(locationManager != null){ 355 locationManager.removeUpdates(GPSServer.this); 356 } 357 } 358 359 /** 360 * Function to get latitude 361 * */ getLatitude()362 public double getLatitude(){ 363 if(mLastLocation != null){ 364 latitude = mLastLocation.getLatitude(); 365 } 366 367 // return latitude 368 return latitude; 369 } 370 371 /** 372 * Function to get longitude 373 * */ getLongitude()374 public double getLongitude(){ 375 if(mLastLocation != null){ 376 longitude = mLastLocation.getLongitude(); 377 } 378 379 // return longitude 380 return longitude; 381 } 382 383 /** 384 * Function to check GPS/wifi enabled 385 * @return boolean 386 * */ canGetLocation()387 public boolean canGetLocation() { 388 return this.canGetLocation; 389 } 390 391 /** 392 * Function to show settings alert dialog 393 * On pressing Settings button will lauch Settings Options 394 * */ showSettingsAlert()395 public void showSettingsAlert(){ 396 AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); 397 398 // Setting Dialog Title 399 alertDialog.setTitle("GPS is settings"); 400 401 // Setting Dialog Message 402 alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?"); 403 404 // On pressing Settings button 405 alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() { 406 public void onClick(DialogInterface dialog,int which) { 407 Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 408 mContext.startActivity(intent); 409 } 410 }); 411 412 // on pressing cancel button 413 alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 414 public void onClick(DialogInterface dialog, int which) { 415 dialog.cancel(); 416 } 417 }); 418 419 // Showing Alert Message 420 alertDialog.show(); 421 } 422 423 @Override onLocationChanged(Location location)424 public void onLocationChanged(Location location) { 425 Log.i("DEBUGGER_TAG", "onLocationChanged"); 426 if (location == null) return; 427 428 mLastLocationMillis = SystemClock.elapsedRealtime(); 429 430 mLastLocation = location; 431 } 432 433 @Override onProviderDisabled(String provider)434 public void onProviderDisabled(String provider) { 435 Log.i("DEBUGGER_TAG", "onProviderDisabled " + provider); 436 437 } 438 439 @Override onProviderEnabled(String provider)440 public void onProviderEnabled(String provider) { 441 Log.i("DEBUGGER_TAG", "onProviderEnabled " + provider); 442 443 } 444 445 @Override onStatusChanged(String provider, int status, Bundle extras)446 public void onStatusChanged(String provider, int status, Bundle extras) { 447 Log.i("DEBUGGER_TAG", "onStatusChanged"); 448 449 } 450 451 452 @Override onBind(Intent arg0)453 public IBinder onBind(Intent arg0) { 454 return null; 455 } 456 createRMC()457 private String createRMC(){ 458 // Create an NMEA sentence 459 String s = "$LCRMC,,"; 460 if(isGPSFix) 461 s = s.concat("A,"); 462 else 463 s = s.concat("V,"); 464 465 466 String slat = ""; 467 double ltt = latitude; 468 if(latitude < 0) 469 ltt = -latitude; 470 471 double d0 = Math.floor(ltt); 472 double d1 = ltt-d0; 473 double d2 = Math.floor(d1 * 60); 474 double d3 = (d1*60.) - d2; 475 476 slat = slat.format("%.0f.%.0f,", (d0 * 100.) + d2, d3 * 10000); 477 478 if(latitude > 0) 479 slat = slat.concat("N,"); 480 else 481 slat = slat.concat("S,"); 482 483 484 s = s.concat(slat); 485 486 String slon = ""; 487 double lot = longitude; 488 if(longitude < 0) 489 lot = -longitude; 490 491 d0 = Math.floor(lot); 492 d1 = lot-d0; 493 d2 = Math.floor(d1 * 60); 494 d3 = (d1*60.) - d2; 495 496 if(d0 < 100.) 497 slon = "0"; 498 slon = slon.concat(slon.format("%.0f.%.0f,", (d0 * 100.) + d2, d3 * 10000)); 499 500 if(longitude > 0) 501 slon = slon.concat("E,"); 502 else 503 slon = slon.concat("W,"); 504 505 s = s.concat(slon); 506 507 String sspeed = ""; 508 sspeed = sspeed.format("%.2f,", speed /.5144); 509 s = s.concat(sspeed); 510 511 String strack = ""; 512 strack = strack.format("%.0f,", course); 513 s = s.concat(strack); 514 515 s = s.concat(",,,"); // unused fields 516 517 s = s.concat("*55"); // checksum 518 519 // s = s.concat("\r\n"); 520 521 Log.i("DEBUGGER_TAG", s); 522 523 return s; 524 } 525 } 526 527 528 529 //GPSTracker gps = new GPSTracker(this); 530 //if(gps.canGetLocation()){ // gps enabled} // return boolean true/false 531 532 //Getting Latitude and Longitude 533 //gps.getLatitude(); // returns latitude 534 //gps.getLongitude(); // returns longitude 535 536 //Showing GPS Settings Alert Dialog 537 //gps.showSettingsAlert(); 538 539 //Stop using GPS 540 //gps.stopUsingGPS(); 541