diff --git a/locationTrack/.gitignore b/locationTrack/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/locationTrack/.gitignore @@ -0,0 +1 @@ +/build diff --git a/locationTrack/build.gradle b/locationTrack/build.gradle new file mode 100644 index 0000000..f09c1e7 --- /dev/null +++ b/locationTrack/build.gradle @@ -0,0 +1,35 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 27 + + + + defaultConfig { + applicationId "app.prior.lollipop" + minSdkVersion 15 + targetSdkVersion 27 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar']) + + implementation 'com.android.support:appcompat-v7:27.0.2' + implementation 'com.android.support:design:27.0.2' + implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.google.android.gms:play-services-location:11.8.0' +} diff --git a/locationTrack/proguard-rules.pro b/locationTrack/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/locationTrack/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/locationTrack/src/androidTest/java/app/prior/tracker/ExampleInstrumentedTest.java b/locationTrack/src/androidTest/java/app/prior/tracker/ExampleInstrumentedTest.java new file mode 100644 index 0000000..d0f8642 --- /dev/null +++ b/locationTrack/src/androidTest/java/app/prior/tracker/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package app.prior.tracker; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("app.prior.lollipop", appContext.getPackageName()); + } +} diff --git a/locationTrack/src/main/AndroidManifest.xml b/locationTrack/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5c89004 --- /dev/null +++ b/locationTrack/src/main/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/locationTrack/src/main/java/app/prior/tracker/LocationUpdatesComponent.java b/locationTrack/src/main/java/app/prior/tracker/LocationUpdatesComponent.java new file mode 100644 index 0000000..8214c6a --- /dev/null +++ b/locationTrack/src/main/java/app/prior/tracker/LocationUpdatesComponent.java @@ -0,0 +1,181 @@ +package app.prior.tracker; + +import android.content.Context; +import android.location.Location; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.util.Log; + +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationCallback; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationResult; +import com.google.android.gms.location.LocationServices; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; + + +/** + * stand alone component for location updates + */ +public class LocationUpdatesComponent { + + private static final String TAG = LocationUpdatesComponent.class.getSimpleName(); + + /** + * The desired interval for location updates. Inexact. Updates may be more or less frequent. + */ + private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 3 * 1000; + + /** + * The fastest rate for active location updates. Updates will never be more frequent + * than this value. + */ + private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = + UPDATE_INTERVAL_IN_MILLISECONDS / 2; + + private LocationRequest mLocationRequest; + + /** + * Provides access to the Fused Location Provider API. + */ + private FusedLocationProviderClient mFusedLocationClient; + + /** + * Callback for changes in location. + */ + private LocationCallback mLocationCallback; + + /** + * The current location. + */ + private Location mLocation; + + public ILocationProvider iLocationProvider; + + public LocationUpdatesComponent(ILocationProvider iLocationProvider) { + this.iLocationProvider = iLocationProvider; + } + + /** + * create first time to initialize the location components + * + * @param context + */ + public void onCreate(Context context) { + Log.i(TAG, "created..............."); + mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context); + + mLocationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + super.onLocationResult(locationResult); + Log.i(TAG, "onCreate...onLocationResult...............loc " + locationResult.getLastLocation()); + + onNewLocation(locationResult.getLastLocation()); + } + }; + // create location request + createLocationRequest(); + // get last known location + getLastLocation(); + } + + /** + * start location updates + */ + public void onStart() { + Log.i(TAG, "onStart "); + //hey request for location updates + requestLocationUpdates(); + } + + /** + * remove location updates + */ + public void onStop() { + Log.i(TAG, "onStop...."); + removeLocationUpdates(); + } + + /** + * Makes a request for location updates. Note that in this sample we merely log the + * {@link SecurityException}. + */ + public void requestLocationUpdates() { + Log.i(TAG, "Requesting location updates"); + try { + mFusedLocationClient.requestLocationUpdates(mLocationRequest, + mLocationCallback, Looper.getMainLooper()); + } catch (SecurityException unlikely) { + Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely); + } + } + + /** + * Removes location updates. Note that in this sample we merely log the + * {@link SecurityException}. + */ + public void removeLocationUpdates() { + Log.i(TAG, "Removing location updates"); + try { + mFusedLocationClient.removeLocationUpdates(mLocationCallback); +// Utils.setRequestingLocationUpdates(this, false); +// stopSelf(); + } catch (SecurityException unlikely) { +// Utils.setRequestingLocationUpdates(this, true); + Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely); + } + } + + /** + * get last location + */ + private void getLastLocation() { + try { + mFusedLocationClient.getLastLocation() + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful() && task.getResult() != null) { + mLocation = task.getResult(); + Log.i(TAG, "getLastLocation " + mLocation); +// Toast.makeText(getApplicationContext(), "" + mLocation, Toast.LENGTH_SHORT).show(); + onNewLocation(mLocation); + } else { + Log.w(TAG, "Failed to get location."); + } + } + }); + } catch (SecurityException unlikely) { + Log.e(TAG, "Lost location permission." + unlikely); + } + } + + private void onNewLocation(Location location) { + Log.i(TAG, "New location: " + location); +// Toast.makeText(getApplicationContext(), "onNewLocation " + location, Toast.LENGTH_LONG).show(); + + mLocation = location; + if (this.iLocationProvider != null) { + this.iLocationProvider.onLocationUpdate(mLocation); + } + } + + /** + * Sets the location request parameters. + */ + private void createLocationRequest() { + mLocationRequest = new LocationRequest(); + mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); + mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); + mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + } + + /** + * implements this interface to get call back of location changes + */ + public interface ILocationProvider { + void onLocationUpdate(Location location); + } +} \ No newline at end of file diff --git a/locationTrack/src/main/java/app/prior/tracker/MainActivity.java b/locationTrack/src/main/java/app/prior/tracker/MainActivity.java new file mode 100644 index 0000000..d695594 --- /dev/null +++ b/locationTrack/src/main/java/app/prior/tracker/MainActivity.java @@ -0,0 +1,130 @@ +package app.prior.tracker; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Location; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.support.annotation.NonNull; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import java.text.DateFormat; +import java.util.Date; + +public class MainActivity extends AppCompatActivity { + + private static final String TAG = MainActivity.class.getSimpleName(); + + private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; + public static final String MESSENGER_INTENT_KEY = "msg-intent-key"; + + // as google doc says + // Handler for incoming messages from the service. + private IncomingMessageHandler mHandler; + private TextView locationMsg; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + locationMsg = findViewById(R.id.location); + + findViewById(R.id.start).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + requestPermissions(); + + } + }); + + findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + stopService(new Intent(MainActivity.this, MyIntentService.class)); + } + }); + + mHandler = new IncomingMessageHandler(); + + } + + private void requestPermissions() { + boolean shouldProvideRationale = + ActivityCompat.shouldShowRequestPermissionRationale(this, + Manifest.permission.ACCESS_FINE_LOCATION); + + // Provide an additional rationale to the user. This would happen if the user denied the + // request previously, but didn't check the "Don't ask again" checkbox. + if (shouldProvideRationale) { + Log.i(TAG, "Displaying permission rationale to provide additional context."); + // Request permission + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERMISSIONS_REQUEST_CODE); + } else { + Log.i(TAG, "Requesting permission"); + // Request permission. It's possible this can be auto answered if device policy + // sets the permission in a given state or the user denied the permission + // previously and checked "Never ask again". + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERMISSIONS_REQUEST_CODE); + } + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + Log.i(TAG, "onRequestPermissionResult grantResults " + grantResults); + if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) { + if (grantResults.length <= 0) { + // If user interaction was interrupted, the permission request is cancelled and you + // receive empty arrays. + Log.i(TAG, "User interaction was cancelled."); + finish(); + } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // can be schedule in this way also + // Utils.scheduleJob(this, LocationUpdatesService.class); + //doing this way to communicate via messenger + // Start service and provide it a way to communicate with this class. + Intent startServiceIntent = new Intent(this, MyIntentService.class); + Messenger messengerIncoming = new Messenger(mHandler); + startServiceIntent.putExtra(MESSENGER_INTENT_KEY, messengerIncoming); + startService(startServiceIntent); + } else { + // Permission denied. + finish(); + } + } + } + + class IncomingMessageHandler extends Handler { + @Override + public void handleMessage(Message msg) { + Log.i(TAG, "handleMessage..." + msg.toString()); + + super.handleMessage(msg); + + switch (msg.what) { + case MyIntentService.LOCATION_MESSAGE: + Location obj = (Location) msg.obj; + String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date()); + locationMsg.setText("LAT : " + obj.getLatitude() + "\nLNG : " + obj.getLongitude() + "\n\n" + obj.toString() + " \n\n\nLast updated- " + currentDateTimeString); + break; + } + } + } + + +} diff --git a/locationTrack/src/main/java/app/prior/tracker/MyIntentService.java b/locationTrack/src/main/java/app/prior/tracker/MyIntentService.java new file mode 100644 index 0000000..7875aea --- /dev/null +++ b/locationTrack/src/main/java/app/prior/tracker/MyIntentService.java @@ -0,0 +1,94 @@ +package app.prior.tracker; + +import android.app.IntentService; +import android.content.Intent; +import android.location.Location; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +import static app.prior.tracker.MainActivity.MESSENGER_INTENT_KEY; + +/** + * An {@link IntentService} subclass for handling asynchronous task requests in + * a service on a separate handler thread. + *

+ * helper methods. + */ +public class MyIntentService extends IntentService implements LocationUpdatesComponent.ILocationProvider { + private static final String TAG = MyIntentService.class.getSimpleName(); + public static final int LOCATION_MESSAGE = 999; + + private LocationUpdatesComponent locationUpdatesComponent; + private Messenger mActivityMessenger; + + public MyIntentService() { + super("MyIntentService"); + } + + @Override + public void onCreate() { + super.onCreate(); + Log.i(TAG, "onCreate "); + + locationUpdatesComponent = new LocationUpdatesComponent(this); + locationUpdatesComponent.onCreate(this); + } + + // this makes service running continuously,,commenting this start command method service runs only once + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.i(TAG, "onStartCommand Service started...."); + if (intent != null) { + mActivityMessenger = intent.getParcelableExtra(MESSENGER_INTENT_KEY); + } + + //hey request for location updates + locationUpdatesComponent.onStart(); + return START_STICKY; + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.i(TAG, "onDestroy..."); + + locationUpdatesComponent.onStop(); + } + + @Override + protected void onHandleIntent(Intent intent) { + Log.i(TAG, "onHandleIntent" + intent); + if (intent != null) { + final String action = intent.getAction(); + } + } + + /** + * send message by using messenger + * + * @param messageID + */ + private void sendMessage(int messageID, Location location) { + // If this service is launched by the JobScheduler, there's no callback Messenger. It + // only exists when the MainActivity calls startService() with the callback in the Intent. + if (mActivityMessenger == null) { + Log.d(TAG, "Service is bound, not started. There's no callback to send a message to."); + return; + } + Message m = Message.obtain(); + m.what = messageID; + m.obj = location; + try { + mActivityMessenger.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Error passing service object back to activity."); + } + } + + @Override + public void onLocationUpdate(Location location) { + sendMessage(LOCATION_MESSAGE, location); + } +} diff --git a/locationTrack/src/main/res/drawable-v24/ic_launcher_foreground.xml b/locationTrack/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/locationTrack/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/locationTrack/src/main/res/drawable/ic_launcher_background.xml b/locationTrack/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/locationTrack/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/locationTrack/src/main/res/layout/activity_main.xml b/locationTrack/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..bea2047 --- /dev/null +++ b/locationTrack/src/main/res/layout/activity_main.xml @@ -0,0 +1,51 @@ + + + + + +