Advertisement
Guest User

Untitled

a guest
Sep 25th, 2018
86
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.24 KB | None | 0 0
  1. package com.kotlin.anggie.transporttracker;
  2.  
  3. import android.annotation.SuppressLint;
  4. import android.app.NotificationManager;
  5. import android.app.PendingIntent;
  6. import android.app.Service;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.IntentFilter;
  10. import android.content.SharedPreferences;
  11. import android.content.pm.PackageManager;
  12. import android.location.Location;
  13. import android.net.ConnectivityManager;
  14. import android.net.NetworkInfo;
  15. import android.os.BatteryManager;
  16. import android.os.Build;
  17. import android.os.Bundle;
  18. import android.os.Environment;
  19. import android.os.IBinder;
  20. import android.os.PowerManager;
  21. import android.support.annotation.RequiresApi;
  22. import android.support.v4.app.ActivityCompat;
  23. import android.support.v4.app.NotificationCompat;
  24. import android.support.v4.content.LocalBroadcastManager;
  25. import android.util.Log;
  26. import android.widget.Toast;
  27.  
  28. import com.google.android.gms.common.api.GoogleApiClient;
  29. import com.google.android.gms.gcm.GcmNetworkManager;
  30. import com.google.android.gms.gcm.OneoffTask;
  31. import com.google.android.gms.location.LocationListener;
  32. import com.google.android.gms.location.LocationRequest;
  33. import com.google.android.gms.location.LocationServices;
  34. import com.google.android.gms.tasks.OnCompleteListener;
  35. import com.google.android.gms.tasks.OnSuccessListener;
  36. import com.google.android.gms.tasks.Task;
  37. import com.google.firebase.analytics.FirebaseAnalytics;
  38. import com.google.firebase.auth.AuthResult;
  39. import com.google.firebase.auth.FirebaseAuth;
  40. import com.google.firebase.database.DataSnapshot;
  41. import com.google.firebase.database.DatabaseError;
  42. import com.google.firebase.database.DatabaseReference;
  43. import com.google.firebase.database.FirebaseDatabase;
  44. import com.google.firebase.database.ValueEventListener;
  45. import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
  46. import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
  47.  
  48. import java.io.File;
  49. import java.io.FileWriter;
  50. import java.text.SimpleDateFormat;
  51. import java.util.Calendar;
  52. import java.util.Date;
  53. import java.util.HashMap;
  54. import java.util.LinkedList;
  55. import java.util.Map;
  56.  
  57. public class TrackerService extends Service implements LocationListener {
  58. private static final String TAG = TrackerService.class.getSimpleName();
  59. public static final String STATUS_INTENT = "status";
  60.  
  61. private static final int NOTIFICATION_ID = 1;
  62. private static final int FOREGROUND_SERVICE_ID = 1;
  63. private static final int CONFIG_CACHE_EXPIRY = 600; // 10 minutes.
  64.  
  65. private GoogleApiClient mGoogleApiClient;
  66. private DatabaseReference mFirebaseTransportRef;
  67. private FirebaseRemoteConfig mFirebaseRemoteConfig;
  68. private LinkedList<Map<String, Object>> mTransportStatuses = new LinkedList<>();
  69. private NotificationManager mNotificationManager;
  70. private NotificationCompat.Builder mNotificationBuilder;
  71. private PowerManager.WakeLock mWakelock;
  72.  
  73. private SharedPreferences mPrefs;
  74.  
  75. public TrackerService() {
  76.  
  77. }
  78.  
  79. @Override
  80. public IBinder onBind(Intent intent) {
  81. return null;
  82. }
  83.  
  84. @RequiresApi(api = Build.VERSION_CODES.M)
  85. @Override
  86. public void onCreate() {
  87. super.onCreate();
  88.  
  89. buildNotification();
  90. setStatusMessage(R.string.connecting);
  91.  
  92. mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
  93. FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
  94. .setDeveloperModeEnabled(BuildConfig.DEBUG)
  95. .build();
  96. mFirebaseRemoteConfig.setConfigSettings(configSettings);
  97. mFirebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults);
  98.  
  99. mPrefs = getSharedPreferences(getString(R.string.prefs), MODE_PRIVATE);
  100. String email = mPrefs.getString(getString(R.string.email), "");
  101. String password = mPrefs.getString(getString(R.string.password), "");
  102. authenticate(email, password);
  103.  
  104. }
  105.  
  106. @Override
  107. public void onDestroy() {
  108. // Set activity title to not tracking.
  109. setStatusMessage(R.string.not_tracking);
  110. // Stop the persistent notification.
  111. mNotificationManager.cancel(NOTIFICATION_ID);
  112. // Stop receiving location updates.
  113. if (mGoogleApiClient != null) {
  114. LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
  115. TrackerService.this);
  116. }
  117. // Release the wakelock
  118. if (mWakelock != null) {
  119. mWakelock.release();
  120. }
  121. super.onDestroy();
  122. }
  123.  
  124. private void authenticate(String email, String password) {
  125. final FirebaseAuth mAuth = FirebaseAuth.getInstance();
  126. mAuth.signInWithEmailAndPassword(email, password)
  127. .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
  128. @Override
  129. public void onComplete(Task<AuthResult> task) {
  130. Log.i(TAG, "authenticate: " + task.isSuccessful());
  131. if (task.isSuccessful()) {
  132. fetchRemoteConfig();
  133. loadPreviousStatuses();
  134. } else {
  135. Toast.makeText(TrackerService.this, R.string.auth_failed,
  136. Toast.LENGTH_SHORT).show();
  137. stopSelf();
  138. }
  139. }
  140. });
  141. }
  142.  
  143. private void fetchRemoteConfig() {
  144. long cacheExpiration = CONFIG_CACHE_EXPIRY;
  145. if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
  146. cacheExpiration = 0;
  147. }
  148. mFirebaseRemoteConfig.fetch(cacheExpiration)
  149. .addOnSuccessListener(new OnSuccessListener<Void>() {
  150. @Override
  151. public void onSuccess(Void aVoid) {
  152. Log.i(TAG, "Remote config fetched");
  153. mFirebaseRemoteConfig.activateFetched();
  154. }
  155. });
  156. }
  157.  
  158. /**
  159. * Loads previously stored statuses from Firebase, and once retrieved,
  160. * start location tracking.
  161. */
  162. private void loadPreviousStatuses() {
  163. String transportId = mPrefs.getString(getString(R.string.transport_id), "");
  164. FirebaseAnalytics.getInstance(this).setUserProperty("transportID", transportId);
  165. String path = getString(R.string.firebase_path) + transportId;
  166. mFirebaseTransportRef = FirebaseDatabase.getInstance().getReference(path);
  167. mFirebaseTransportRef.addListenerForSingleValueEvent(new ValueEventListener() {
  168. @Override
  169. public void onDataChange(DataSnapshot snapshot) {
  170. if (snapshot != null) {
  171. for (DataSnapshot transportStatus : snapshot.getChildren()) {
  172. mTransportStatuses.add(Integer.parseInt(transportStatus.getKey()),
  173. (Map<String, Object>) transportStatus.getValue());
  174. }
  175. }
  176. startLocationTracking();
  177. }
  178.  
  179. @Override
  180. public void onCancelled(DatabaseError error) {
  181. // TODO: Handle gracefully
  182. }
  183. });
  184. }
  185.  
  186. private GoogleApiClient.ConnectionCallbacks mLocationRequestCallback = new GoogleApiClient
  187. .ConnectionCallbacks() {
  188.  
  189. @SuppressLint("InvalidWakeLockTag")
  190. @Override
  191. public void onConnected(Bundle bundle) {
  192. LocationRequest request = new LocationRequest();
  193. request.setInterval(mFirebaseRemoteConfig.getLong("LOCATION_REQUEST_INTERVAL"));
  194. request.setFastestInterval(mFirebaseRemoteConfig.getLong
  195. ("LOCATION_REQUEST_INTERVAL_FASTEST"));
  196. request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
  197. if (ActivityCompat.checkSelfPermission(TrackerService.this,
  198. android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
  199. ActivityCompat.checkSelfPermission(TrackerService.this, android.Manifest.permission.ACCESS_COARSE_LOCATION)
  200. != PackageManager.PERMISSION_GRANTED) {
  201. // TODO: Consider calling
  202. // ActivityCompat#requestPermissions
  203. // here to request the missing permissions, and then overriding
  204. // public void onRequestPermissionsResult(int requestCode, String[] permissions,
  205. // int[] grantResults)
  206. // to handle the case where the user grants the permission. See the documentation
  207. // for ActivityCompat#requestPermissions for more details.
  208. return;
  209. }
  210. LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
  211. request, TrackerService.this);
  212. setStatusMessage(R.string.tracking);
  213.  
  214. // Hold a partial wake lock to keep CPU awake when the we're tracking location.
  215. PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
  216. mWakelock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag");
  217. mWakelock.acquire();
  218. }
  219.  
  220. @Override
  221. public void onConnectionSuspended(int reason) {
  222. // TODO: Handle gracefully
  223. }
  224. };
  225.  
  226. /**
  227. * Starts location tracking by creating a Google API client, and
  228. * requesting location updates.
  229. */
  230. private void startLocationTracking() {
  231. mGoogleApiClient = new GoogleApiClient.Builder(this)
  232. .addConnectionCallbacks(mLocationRequestCallback)
  233. .addApi(LocationServices.API)
  234. .build();
  235. mGoogleApiClient.connect();
  236. }
  237.  
  238. /**
  239. * Determines if the current location is approximately the same as the location
  240. * for a particular status. Used to check if we'll add a new status, or
  241. * update the most recent status of we're stationary.
  242. */
  243. private boolean locationIsAtStatus(Location location, int statusIndex) {
  244. if (mTransportStatuses.size() <= statusIndex) {
  245. return false;
  246. }
  247. Map<String, Object> status = mTransportStatuses.get(statusIndex);
  248. Location locationForStatus = new Location("");
  249. locationForStatus.setLatitude((double) status.get("lat"));
  250. locationForStatus.setLongitude((double) status.get("lng"));
  251. float distance = location.distanceTo(locationForStatus);
  252. Log.d(TAG, String.format("Distance from status %s is %sm", statusIndex, distance));
  253. return distance < mFirebaseRemoteConfig.getLong("LOCATION_MIN_DISTANCE_CHANGED");
  254. }
  255.  
  256. private float getBatteryLevel() {
  257. Intent batteryStatus = registerReceiver(null,
  258. new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
  259. int batteryLevel = -1;
  260. int batteryScale = 1;
  261. if (batteryStatus != null) {
  262. batteryLevel = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, batteryLevel);
  263. batteryScale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, batteryScale);
  264. }
  265. return batteryLevel / (float) batteryScale * 100;
  266. }
  267.  
  268. private void logStatusToStorage(Map<String, Object> transportStatus) {
  269. try {
  270. File path = new File(Environment.getExternalStoragePublicDirectory(""),
  271. "transport-tracker-log.txt");
  272. if (!path.exists()) {
  273. path.createNewFile();
  274. }
  275. FileWriter logFile = new FileWriter(path.getAbsolutePath(), true);
  276. logFile.append(transportStatus.toString() + "\n");
  277. logFile.close();
  278. } catch (Exception e) {
  279. Log.e(TAG, "Log file error", e);
  280. }
  281. }
  282.  
  283. private void shutdownAndScheduleStartup(int when) {
  284. Log.i(TAG, "overnight shutdown, seconds to startup: " + when);
  285. com.google.android.gms.gcm.Task task = new OneoffTask.Builder()
  286. .setService(TrackerTaskService.class)
  287. .setExecutionWindow(when, when + 60)
  288. .setUpdateCurrent(true)
  289. .setTag(TrackerTaskService.TAG)
  290. .setRequiredNetwork(com.google.android.gms.gcm.Task.NETWORK_STATE_ANY)
  291. .setRequiresCharging(false)
  292. .build();
  293. GcmNetworkManager.getInstance(this).schedule(task);
  294. stopSelf();
  295. }
  296.  
  297. /**
  298. * Pushes a new status to Firebase when location changes.
  299. */
  300. @Override
  301. public void onLocationChanged(Location location) {
  302.  
  303. fetchRemoteConfig();
  304.  
  305. long hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
  306. int startupSeconds = (int) (mFirebaseRemoteConfig.getDouble("SLEEP_HOURS_DURATION") * 3600);
  307. if (hour == mFirebaseRemoteConfig.getLong("SLEEP_HOUR_OF_DAY")) {
  308. shutdownAndScheduleStartup(startupSeconds);
  309. return;
  310. }
  311.  
  312. Map<String, Object> transportStatus = new HashMap<>();
  313. transportStatus.put("lat", location.getLatitude());
  314. transportStatus.put("lng", location.getLongitude());
  315. transportStatus.put("time", new Date().getTime());
  316. transportStatus.put("power", getBatteryLevel());
  317.  
  318. if (locationIsAtStatus(location, 1) && locationIsAtStatus(location, 0)) {
  319. // If the most recent two statuses are approximately at the same
  320. // location as the new current location, rather than adding the new
  321. // location, we update the latest status with the current. Two statuses
  322. // are kept when the locations are the same, the earlier representing
  323. // the time the location was arrived at, and the latest representing the
  324. // current time.
  325. mTransportStatuses.set(0, transportStatus);
  326. // Only need to update 0th status, so we can save bandwidth.
  327.  
  328. mFirebaseTransportRef.child("0").setValue(transportStatus);
  329. } else {
  330. // Maintain a fixed number of previous statuses.
  331. while (mTransportStatuses.size() >= mFirebaseRemoteConfig.getLong("MAX_STATUSES")) {
  332. mTransportStatuses.removeLast();
  333. }
  334. mTransportStatuses.addFirst(transportStatus);
  335. // We push the entire list at once since each key/index changes, to
  336. // minimize network requests.
  337. mFirebaseTransportRef.setValue(mTransportStatuses);
  338. }
  339.  
  340. if (BuildConfig.DEBUG) {
  341. logStatusToStorage(transportStatus);
  342. }
  343.  
  344. NetworkInfo info = ((ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE))
  345. .getActiveNetworkInfo();
  346. boolean connected = info != null && info.isConnectedOrConnecting();
  347. setStatusMessage(connected ? R.string.tracking : R.string.not_tracking);
  348. }
  349.  
  350. @RequiresApi(api = Build.VERSION_CODES.M)
  351. private void buildNotification() {
  352. mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
  353. PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
  354. new Intent(this, TrackerActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
  355. mNotificationBuilder = new NotificationCompat.Builder(this)
  356. .setSmallIcon(R.drawable.bus_white)
  357. .setColor(getColor(R.color.colorPrimary))
  358. .setContentTitle(getString(R.string.app_name))
  359. .setOngoing(true)
  360. .setContentIntent(resultPendingIntent);
  361. startForeground(FOREGROUND_SERVICE_ID, mNotificationBuilder.build());
  362. }
  363.  
  364. /**
  365. * Sets the current status message (connecting/tracking/not tracking).
  366. */
  367. private void setStatusMessage(int stringId) {
  368.  
  369. mNotificationBuilder.setContentText(getString(stringId));
  370. mNotificationManager.notify(NOTIFICATION_ID, mNotificationBuilder.build());
  371.  
  372. // Also display the status message in the activity.
  373. Intent intent = new Intent(STATUS_INTENT);
  374. intent.putExtra(getString(R.string.status), stringId);
  375. LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
  376. }
  377. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement