diff --git a/ActivityRecognition/app/build.gradle b/ActivityRecognition/app/build.gradle
index e27fca5a..0ea869b6 100644
--- a/ActivityRecognition/app/build.gradle
+++ b/ActivityRecognition/app/build.gradle
@@ -22,7 +22,7 @@ dependencies {
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1', {
exclude group: 'com.android.support', module: 'support-annotations'
})
- implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.appcompat:appcompat:1.2.0'
testImplementation 'junit:junit:4.12'
implementation 'com.google.android.material:material:1.0.0'
diff --git a/BasicLocationKotlin/app/build.gradle b/BasicLocationKotlin/app/build.gradle
index b4cc2ab2..933cc389 100644
--- a/BasicLocationKotlin/app/build.gradle
+++ b/BasicLocationKotlin/app/build.gradle
@@ -5,7 +5,6 @@ apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion parent.ext.compileSdkVersion
-
defaultConfig {
applicationId "com.google.android.gms.location.sample.basiclocationsample"
minSdkVersion parent.ext.minSdkVersion
@@ -19,11 +18,35 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+ext {
+ // Android Jetpack
+ // AppCompat
+ appCompatVersion = "1.2.0"
+ // MaterialComponents
+ materialComponentsVersion = "1.2.1"
+
+ // Google PlayServices
+ // Location
+ locationVersion = "17.1.0"
}
dependencies {
- implementation 'androidx.appcompat:appcompat:1.1.0'
- implementation 'com.google.android.material:material:1.0.0'
- implementation "com.google.android.gms:play-services-location:17.0.0"
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
+ // Android Jetpack
+ // AppCompat
+ implementation "androidx.appcompat:appcompat:$appCompatVersion"
+ // MaterialComponents
+ implementation "com.google.android.material:material:$materialComponentsVersion"
+
+ // Google PlayServices
+ // Location
+ implementation "com.google.android.gms:play-services-location:$locationVersion"
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
}
diff --git a/BasicLocationKotlin/app/src/main/java/com/google/android/gms/location/sample/basiclocationsample/MainActivity.kt b/BasicLocationKotlin/app/src/main/java/com/google/android/gms/location/sample/basiclocationsample/MainActivity.kt
index 9231ab4d..e57d8d93 100644
--- a/BasicLocationKotlin/app/src/main/java/com/google/android/gms/location/sample/basiclocationsample/MainActivity.kt
+++ b/BasicLocationKotlin/app/src/main/java/com/google/android/gms/location/sample/basiclocationsample/MainActivity.kt
@@ -16,7 +16,9 @@
package com.google.android.gms.location.sample.basiclocationsample
+import android.Manifest
import android.Manifest.permission.ACCESS_COARSE_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
@@ -24,26 +26,22 @@ import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.location.Location
import android.net.Uri
import android.os.Bundle
+import android.os.Looper
import android.provider.Settings
-import com.google.android.material.snackbar.Snackbar
-import com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE
-import com.google.android.material.snackbar.Snackbar.LENGTH_LONG
-import androidx.core.app.ActivityCompat
-import androidx.appcompat.app.AppCompatActivity
import android.util.Log
import android.view.View
-import android.widget.TextView
-
-import com.google.android.gms.location.FusedLocationProviderClient
-import com.google.android.gms.location.LocationServices
-import com.google.android.gms.location.sample.basiclocationsample.BuildConfig.APPLICATION_ID
-import com.google.android.gms.tasks.Task
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.viewbinding.BuildConfig
+import com.google.android.gms.location.*
+import com.google.android.gms.location.sample.basiclocationsample.databinding.MainActivityBinding
+import com.google.android.material.snackbar.Snackbar
+import com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE
/**
* Demonstrates use of the Location API to retrieve the last known location for a device.
*/
-class MainActivity : AppCompatActivity() {
-
+class MainActivity: AppCompatActivity() {
private val TAG = "MainActivity"
private val REQUEST_PERMISSIONS_REQUEST_CODE = 34
@@ -51,20 +49,23 @@ class MainActivity : AppCompatActivity() {
* Provides the entry point to the Fused Location Provider API.
*/
private lateinit var fusedLocationClient: FusedLocationProviderClient
-
- private lateinit var latitudeText: TextView
- private lateinit var longitudeText: TextView
+
+ /**
+ * ViewBinding
+ */
+ private lateinit var binding: MainActivityBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.main_activity)
-
- latitudeText = findViewById(R.id.latitude_text)
- longitudeText = findViewById(R.id.longitude_text)
-
+
+ // ViewBinding initialization
+ binding = MainActivityBinding.inflate(layoutInflater)
+ val view = binding.root
+ setContentView(view)
+
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
-
+
override fun onStart() {
super.onStart()
@@ -74,75 +75,23 @@ class MainActivity : AppCompatActivity() {
getLastLocation()
}
}
-
- /**
- * Provides a simple way of getting a device's location and is well suited for
- * applications that do not require a fine-grained location and that do not need location
- * updates. Gets the best and most recent location currently available, which may be null
- * in rare cases when a location is not available.
- *
- * Note: this method should be called after location permission has been granted.
- */
- @SuppressLint("MissingPermission")
- private fun getLastLocation() {
- fusedLocationClient.lastLocation
- .addOnCompleteListener { taskLocation ->
- if (taskLocation.isSuccessful && taskLocation.result != null) {
-
- val location = taskLocation.result
-
- latitudeText.text = resources
- .getString(R.string.latitude_label, location?.latitude)
- longitudeText.text = resources
- .getString(R.string.longitude_label, location?.longitude)
- } else {
- Log.w(TAG, "getLastLocation:exception", taskLocation.exception)
- showSnackbar(R.string.no_location_detected)
- }
- }
- }
-
- /**
- * Shows a [Snackbar].
- *
- * @param snackStrId The id for the string resource for the Snackbar text.
- * @param actionStrId The text of the action item.
- * @param listener The listener associated with the Snackbar action.
- */
- private fun showSnackbar(
- snackStrId: Int,
- actionStrId: Int = 0,
- listener: View.OnClickListener? = null
- ) {
- val snackbar = Snackbar.make(findViewById(android.R.id.content), getString(snackStrId),
- LENGTH_INDEFINITE)
- if (actionStrId != 0 && listener != null) {
- snackbar.setAction(getString(actionStrId), listener)
- }
- snackbar.show()
- }
-
/**
* Return the current state of the permissions needed.
*/
- private fun checkPermissions() =
- ActivityCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED
-
- private fun startLocationPermissionRequest() {
- ActivityCompat.requestPermissions(this, arrayOf(ACCESS_COARSE_LOCATION),
- REQUEST_PERMISSIONS_REQUEST_CODE)
- }
-
+ private fun checkPermissions() = ActivityCompat.checkSelfPermission(this,
+ ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
+ this, ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
private fun requestPermissions() {
- if (ActivityCompat.shouldShowRequestPermissionRationale(this, ACCESS_COARSE_LOCATION)) {
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, ACCESS_COARSE_LOCATION)
+ && ActivityCompat.shouldShowRequestPermissionRationale(this, 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.
Log.i(TAG, "Displaying permission rationale to provide additional context.")
- showSnackbar(R.string.permission_rationale, android.R.string.ok, View.OnClickListener {
+ showSnackbar(R.string.permission_rationale, android.R.string.ok) {
// Request permission
startLocationPermissionRequest()
- })
-
+ }
+
} else {
// 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
@@ -151,7 +100,10 @@ class MainActivity : AppCompatActivity() {
startLocationPermissionRequest()
}
}
-
+ private fun startLocationPermissionRequest() {
+ ActivityCompat.requestPermissions(this, arrayOf(ACCESS_COARSE_LOCATION,
+ ACCESS_FINE_LOCATION), REQUEST_PERMISSIONS_REQUEST_CODE)
+ }
/**
* Callback received when a permissions request has been completed.
*/
@@ -166,35 +118,118 @@ class MainActivity : AppCompatActivity() {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
grantResults.isEmpty() -> Log.i(TAG, "User interaction was cancelled.")
-
+
// Permission granted.
- (grantResults[0] == PackageManager.PERMISSION_GRANTED) -> getLastLocation()
-
+ (grantResults[0] == PERMISSION_GRANTED) -> getLastLocation()
+
// Permission denied.
-
+
// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.
-
+
// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
else -> {
- showSnackbar(R.string.permission_denied_explanation, R.string.settings,
- View.OnClickListener {
- // Build intent that displays the App settings screen.
- val intent = Intent().apply {
- action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
- data = Uri.fromParts("package", APPLICATION_ID, null)
- flags = Intent.FLAG_ACTIVITY_NEW_TASK
- }
- startActivity(intent)
- })
+ showSnackbar(R.string.permission_denied_explanation, R.string.settings) {
+ // Build intent that displays the App settings screen.
+ val intent = Intent().apply {
+ action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+ data = Uri.fromParts("package",
+ BuildConfig.LIBRARY_PACKAGE_NAME, null)
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ startActivity(intent)
+ }
}
}
}
}
-
+ /**
+ * Provides a simple way of getting a device's location and is well suited for
+ * applications that do not require a fine-grained location and that do not need location
+ * updates. Gets the best and most recent location currently available, which may be null
+ * in rare cases when a location is not available.
+ *
+ * Note: this method should be called after location permission has been granted.
+ */
+ @SuppressLint("MissingPermission")
+ private fun getLastLocation() {
+ Log.d(TAG, "getLastLocation")
+ fusedLocationClient.lastLocation.addOnCompleteListener { taskLocation ->
+ if (taskLocation.isSuccessful && taskLocation.result != null) {
+ updateViews(taskLocation.result)
+ } else {
+ requestNewLocationData()
+ /*Log.w(TAG, "getLastLocation:exception", taskLocation.exception)
+ showSnackbar(R.string.no_location_detected)*/
+ }
+ }
+ }
+ fun updateViews(currentLocation: Location) {
+ Log.d(TAG, "updateViews")
+ binding.currentLatitude.text = resources
+ .getString(R.string.latitude_label, currentLocation.latitude)
+ binding.currentLongitude.text = resources
+ .getString(R.string.longitude_label, currentLocation.longitude)
+ }
+ fun requestNewLocationData() {
+ Log.d(TAG, "requestNewLocationData")
+ // Initializing LocationRequest
+ // object with appropriate methods
+ val locationRequest = LocationRequest().apply {
+ // For a high level accuracy use PRIORITY_HIGH_ACCURACY argument.
+ // For a low level accuracy (city), use PRIORITY_LOW_POWER
+ priority = LocationRequest.PRIORITY_HIGH_ACCURACY
+ interval = 3
+ fastestInterval = 1
+ numUpdates = 2
+ }
+
+ // setting LocationRequest on a FusedLocationClient
+ fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
+
+ /*if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_DENIED && ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_DENIED) {
+ ActivityCompat.requestPermissions(
+ requireActivity(), arrayOf(Manifest.permission_group.LOCATION), PERMISSIONS_ALLOW_USING_LOCATION_ID
+ )
+ } else {
+ locationClient?.requestLocationUpdates(locationRequest,
+ locationCallback, Looper.myLooper())
+ }*/
+
+ if (ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION)
+ == PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this,
+ ACCESS_COARSE_LOCATION) == PERMISSION_GRANTED) {
+ fusedLocationClient.requestLocationUpdates(locationRequest,
+ locationCallback, Looper.myLooper())
+ }
+ }
+ private val locationCallback: LocationCallback = object : LocationCallback() {
+ override fun onLocationResult(locationResult: LocationResult) {
+ Log.d(TAG, "onLocationResult")
+ updateViews(locationResult.lastLocation)
+ }
+ }
+ /**
+ * Shows a [Snackbar].
+ *
+ * @param snackStrId The id for the string resource for the Snackbar text.
+ * @param actionStrId The text of the action item.
+ * @param listener The listener associated with the Snackbar action.
+ */
+ private fun showSnackbar(snackStrId: Int, actionStrId: Int = 0,
+ listener: View.OnClickListener? = null) {
+ val snackBar = Snackbar.make(findViewById(android.R.id.content), getString(snackStrId),
+ LENGTH_INDEFINITE)
+ if (actionStrId != 0 && listener != null) {
+ snackBar.setAction(getString(actionStrId), listener)
+ }
+ snackBar.show()
+ }
}
diff --git a/BasicLocationKotlin/app/src/main/res/layout/main_activity.xml b/BasicLocationKotlin/app/src/main/res/layout/main_activity.xml
index 85398403..15d15479 100644
--- a/BasicLocationKotlin/app/src/main/res/layout/main_activity.xml
+++ b/BasicLocationKotlin/app/src/main/res/layout/main_activity.xml
@@ -26,7 +26,7 @@
android:paddingTop="@dimen/activity_vertical_margin">
= Build.VERSION_CODES.O) {
- builder.setChannelId(CHANNEL_ID); // Channel ID
+ builder = new NotificationCompat.Builder(
+ this, CHANNEL_ID
+ );
+ // Builder initialization for other versions of Android
+ } else {
+ builder = new NotificationCompat.Builder(
+ this, ""
+ );
}
+
+ builder.addAction(R.drawable.ic_launch, getString(R.string.launch_activity),
+ activityPendingIntent);
+ builder.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
+ servicePendingIntent);
+ builder.setContentText(text);
+ builder.setContentTitle(Utils.getLocationTitle(this));
+ builder.setOngoing(true);
+ builder.setPriority(NotificationCompat.PRIORITY_HIGH);
+ builder.setSmallIcon(R.mipmap.ic_launcher);
+ builder.setTicker(text);
+ builder.setWhen(System.currentTimeMillis());
return builder.build();
}
@@ -301,14 +307,11 @@ private Notification getNotification() {
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();
- } else {
- Log.w(TAG, "Failed to get location.");
- }
+ .addOnCompleteListener(task -> {
+ if (task.isSuccessful() && task.getResult() != null) {
+ mLocation = task.getResult();
+ } else {
+ Log.w(TAG, "Failed to get location.");
}
});
} catch (SecurityException unlikely) {
diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java
index 5244c7a7..3d4e5f91 100644
--- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java
+++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java
@@ -16,6 +16,7 @@
package com.google.android.gms.location.sample.locationupdatesforegroundservice;
+import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -23,28 +24,25 @@
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.location.Location;
-import android.os.IBinder;
-import android.preference.PreferenceManager;
-import androidx.localbroadcastmanager.content.LocalBroadcastManager;
-import androidx.appcompat.app.AppCompatActivity;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.Settings;
import android.util.Log;
+import android.view.View;
+import android.widget.Toast;
-import android.Manifest;
-
-import android.content.pm.PackageManager;
-
-import android.net.Uri;
-
-import android.provider.Settings;
import androidx.annotation.NonNull;
-import com.google.android.material.snackbar.Snackbar;
+import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.preference.PreferenceManager;
+import androidx.viewbinding.BuildConfig;
-import android.view.View;
-import android.widget.Button;
-import android.widget.Toast;
+import com.google.android.gms.location.sample.locationupdatesforegroundservice.databinding.ActivityMainBinding;
+import com.google.android.material.snackbar.Snackbar;
/**
* The only activity in this sample.
@@ -98,8 +96,7 @@ public class MainActivity extends AppCompatActivity implements
private boolean mBound = false;
// UI elements.
- private Button mRequestLocationUpdatesButton;
- private Button mRemoveLocationUpdatesButton;
+ private ActivityMainBinding mBinding;
// Monitors the state of the connection to the service.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@@ -121,12 +118,17 @@ public void onServiceDisconnected(ComponentName name) {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ // ViewBinding initialization
+ mBinding = ActivityMainBinding.inflate(getLayoutInflater());
+ View view = mBinding.getRoot();
+ setContentView(view);
+
myReceiver = new MyReceiver();
- setContentView(R.layout.activity_main);
// Check that the user hasn't revoked permissions by going to Settings.
if (Utils.requestingLocationUpdates(this)) {
- if (!checkPermissions()) {
+ if (checkPermissions()) {
requestPermissions();
}
}
@@ -138,26 +140,16 @@ protected void onStart() {
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
- mRequestLocationUpdatesButton = (Button) findViewById(R.id.request_location_updates_button);
- mRemoveLocationUpdatesButton = (Button) findViewById(R.id.remove_location_updates_button);
-
- mRequestLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (!checkPermissions()) {
- requestPermissions();
- } else {
- mService.requestLocationUpdates();
- }
+ mBinding.requestLocationUpdatesButton.setOnClickListener(view -> {
+ if (checkPermissions()) {
+ requestPermissions();
+ } else {
+ mService.requestLocationUpdates();
}
});
- mRemoveLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mService.removeLocationUpdates();
- }
- });
+ mBinding.removeLocationUpdatesButton.setOnClickListener(
+ view -> mService.removeLocationUpdates());
// Restore the state of the buttons when the activity (re)launches.
setButtonsState(Utils.requestingLocationUpdates(this));
@@ -199,8 +191,8 @@ protected void onStop() {
* Returns the current state of the permissions needed.
*/
private boolean checkPermissions() {
- return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this,
- Manifest.permission.ACCESS_FINE_LOCATION);
+ return PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_FINE_LOCATION);
}
private void requestPermissions() {
@@ -213,17 +205,14 @@ private void requestPermissions() {
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
- findViewById(R.id.activity_main),
+ mBinding.getRoot(),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
- .setAction(R.string.ok, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Request permission
- ActivityCompat.requestPermissions(MainActivity.this,
- new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
- REQUEST_PERMISSIONS_REQUEST_CODE);
- }
+ .setAction(R.string.ok, view -> {
+ // Request permission
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ REQUEST_PERMISSIONS_REQUEST_CODE);
})
.show();
} else {
@@ -256,22 +245,19 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
// Permission denied.
setButtonsState(false);
Snackbar.make(
- findViewById(R.id.activity_main),
+ mBinding.getRoot(),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
- .setAction(R.string.settings, new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- // Build intent that displays the App settings screen.
- 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);
- }
+ .setAction(R.string.settings, view -> {
+ // Build intent that displays the App settings screen.
+ Intent intent = new Intent();
+ intent.setAction(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package",
+ BuildConfig.LIBRARY_PACKAGE_NAME, null);
+ intent.setData(uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
})
.show();
}
@@ -293,9 +279,9 @@ public void onReceive(Context context, Intent intent) {
}
@Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String string) {
// Update the buttons state depending on whether location updates are being requested.
- if (s.equals(Utils.KEY_REQUESTING_LOCATION_UPDATES)) {
+ if (string.equals(Utils.KEY_REQUESTING_LOCATION_UPDATES)) {
setButtonsState(sharedPreferences.getBoolean(Utils.KEY_REQUESTING_LOCATION_UPDATES,
false));
}
@@ -303,11 +289,11 @@ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, Strin
private void setButtonsState(boolean requestingLocationUpdates) {
if (requestingLocationUpdates) {
- mRequestLocationUpdatesButton.setEnabled(false);
- mRemoveLocationUpdatesButton.setEnabled(true);
+ mBinding.requestLocationUpdatesButton.setEnabled(false);
+ mBinding.removeLocationUpdatesButton.setEnabled(true);
} else {
- mRequestLocationUpdatesButton.setEnabled(true);
- mRemoveLocationUpdatesButton.setEnabled(false);
+ mBinding.requestLocationUpdatesButton.setEnabled(true);
+ mBinding.removeLocationUpdatesButton.setEnabled(false);
}
}
}
diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java
index 01dc5887..df98f6ca 100644
--- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java
+++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java
@@ -19,7 +19,8 @@
import android.content.Context;
import android.location.Location;
-import android.preference.PreferenceManager;
+
+import androidx.preference.PreferenceManager;
import java.text.DateFormat;
import java.util.Date;
diff --git a/LocationUpdatesForegroundService/build.gradle b/LocationUpdatesForegroundService/build.gradle
index 095a4ff4..3bd97cd3 100644
--- a/LocationUpdatesForegroundService/build.gradle
+++ b/LocationUpdatesForegroundService/build.gradle
@@ -1,15 +1,17 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
+ ext {
+ // Android Gradle plugin
+ gradlePluginVersion = "4.0.1"
+ }
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.1'
-
- // NOTE: Do not place your application dependencies here; they belong
- // in the individual module build.gradle files
+ // Android Gradle plugin
+ classpath "com.android.tools.build:gradle:$gradlePluginVersion"
}
}
diff --git a/LocationUpdatesForegroundService/gradle.properties b/LocationUpdatesForegroundService/gradle.properties
index aac7c9b4..01236edf 100644
--- a/LocationUpdatesForegroundService/gradle.properties
+++ b/LocationUpdatesForegroundService/gradle.properties
@@ -15,3 +15,5 @@ org.gradle.jvmargs=-Xmx1536m
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
+
+android.useAndroidX=true
diff --git a/LocationUpdatesForegroundService/gradle/wrapper/gradle-wrapper.properties b/LocationUpdatesForegroundService/gradle/wrapper/gradle-wrapper.properties
index 0793d0b0..d936ae58 100644
--- a/LocationUpdatesForegroundService/gradle/wrapper/gradle-wrapper.properties
+++ b/LocationUpdatesForegroundService/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/LocationUpdatesForegroundServiceKotlin/.gitignore b/LocationUpdatesForegroundServiceKotlin/.gitignore
new file mode 100644
index 00000000..e3ee5f5f
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/.gitignore
@@ -0,0 +1,9 @@
+# Directories
+build/
+.idea
+.gradle
+
+# Files
+*.iml
+local.properties
+.DS_Store
\ No newline at end of file
diff --git a/LocationUpdatesForegroundServiceKotlin/.google/packaging.yaml b/LocationUpdatesForegroundServiceKotlin/.google/packaging.yaml
new file mode 100644
index 00000000..eaa5334c
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/.google/packaging.yaml
@@ -0,0 +1,25 @@
+# Copyright 2018 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This file is used by Google as part of our samples packaging process.
+# End users may safely ignore this file. It has no relevance to other systems.
+---
+status: PUBLISHED
+technologies: [Android]
+categories: [Location]
+languages: [Java]
+solutions: [Mobile]
+github: android/location-samples
+level: BEGINNER
+license: apache2
diff --git a/LocationUpdatesForegroundServiceKotlin/app/.gitignore b/LocationUpdatesForegroundServiceKotlin/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/LocationUpdatesForegroundServiceKotlin/app/build.gradle b/LocationUpdatesForegroundServiceKotlin/app/build.gradle
new file mode 100644
index 00000000..e6c0760b
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/build.gradle
@@ -0,0 +1,79 @@
+apply plugin: 'com.android.application'
+
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion 29
+
+ defaultConfig {
+ applicationId "com.google.android.gms.location.sample.locationupdatesforegroundservice"
+ minSdkVersion 16
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility = 1.8
+ targetCompatibility = 1.8
+ }
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+ext {
+ // Jetpack
+ // AppCompat
+ appCompatVersion = "1.2.0"
+ // LocalBroadcastManager
+ localBroadcastManagerVersion = "1.0.0"
+ // Preference
+ preferenceVersion = "1.1.1"
+ // MaterialComponents
+ materialComponentsVersion = "1.2.1"
+
+ // Google
+ // Location
+ locationVersion = "17.0.0"
+
+ // Testing
+ // Junit
+ junitVersion = "4.13"
+ // Espresso
+ espressoVersion = "3.3.0"
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ // Jetpack
+ // AppCompat
+ implementation "androidx.appcompat:appcompat:$appCompatVersion"
+ // LocalBroadcastManager
+ implementation "androidx.localbroadcastmanager:localbroadcastmanager:$localBroadcastManagerVersion"
+ // Preference
+ implementation "androidx.preference:preference-ktx:$preferenceVersion"
+ // MaterialComponents
+ implementation "com.google.android.material:material:$materialComponentsVersion"
+
+ // Google
+ implementation "com.google.android.gms:play-services-location:$locationVersion"
+
+ // Kotlin
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+
+ // Testing
+ // Junit
+ testImplementation "junit:junit:$junitVersion"
+ // Espresso
+ androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion", {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+}
diff --git a/LocationUpdatesForegroundServiceKotlin/app/proguard-rules.pro b/LocationUpdatesForegroundServiceKotlin/app/proguard-rules.pro
new file mode 100644
index 00000000..ec87fa03
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/shailentuli/Library/Android/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/AndroidManifest.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..415ff0f8
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
new file mode 100644
index 00000000..848ce706
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
@@ -0,0 +1,376 @@
+/**
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.gms.location.sample.locationupdatesforegroundservice;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.location.Location;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import androidx.core.app.NotificationCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+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;
+
+/**
+ * A bound and started service that is promoted to a foreground service when location updates have
+ * been requested and all clients unbind.
+ *
+ * For apps running in the background on "O" devices, location is computed only once every 10
+ * minutes and delivered batched every 30 minutes. This restriction applies even to apps
+ * targeting "N" or lower which are run on "O" devices.
+ *
+ * This sample show how to use a long-running service for location updates. When an activity is
+ * bound to this service, frequent location updates are permitted. When the activity is removed
+ * from the foreground, the service promotes itself to a foreground service, and location updates
+ * continue. When the activity comes back to the foreground, the foreground service stops, and the
+ * notification associated with that service is removed.
+ */
+public class LocationUpdatesService extends Service {
+
+ private static final String PACKAGE_NAME =
+ "com.google.android.gms.location.sample.locationupdatesforegroundservice";
+
+ private static final String TAG = LocationUpdatesService.class.getSimpleName();
+
+ /**
+ * The name of the channel for notifications.
+ */
+ private static final String CHANNEL_ID = "channel_01";
+
+ static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
+
+ static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
+ private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
+ ".started_from_notification";
+
+ private final IBinder mBinder = new LocalBinder();
+
+ /**
+ * The desired interval for location updates. Inexact. Updates may be more or less frequent.
+ */
+ private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
+
+ /**
+ * 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;
+
+ /**
+ * The identifier for the notification displayed for the foreground service.
+ */
+ private static final int NOTIFICATION_ID = 12345678;
+
+ /**
+ * Used to check whether the bound activity has really gone away and not unbound as part of an
+ * orientation change. We create a foreground service notification only if the former takes
+ * place.
+ */
+ private boolean mChangingConfiguration = false;
+
+ private NotificationManager mNotificationManager;
+
+ /**
+ * Contains parameters used by {@link com.google.android.gms.location.FusedLocationProviderClient}.
+ */
+ private LocationRequest mLocationRequest;
+
+ /**
+ * Provides access to the Fused Location Provider API.
+ */
+ private FusedLocationProviderClient mFusedLocationClient;
+
+ /**
+ * Callback for changes in location.
+ */
+ private LocationCallback mLocationCallback;
+
+ private Handler mServiceHandler;
+
+ /**
+ * The current location.
+ */
+ private Location mLocation;
+
+ public LocationUpdatesService() {
+ }
+
+ @Override
+ public void onCreate() {
+ mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
+
+ mLocationCallback = new LocationCallback() {
+ @Override
+ public void onLocationResult(LocationResult locationResult) {
+ super.onLocationResult(locationResult);
+ onNewLocation(locationResult.getLastLocation());
+ }
+ };
+
+ createLocationRequest();
+ getLastLocation();
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mServiceHandler = new Handler(handlerThread.getLooper());
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+ // Android O requires a Notification Channel.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ CharSequence name = getString(R.string.app_name);
+ // Create the channel for the notification
+ NotificationChannel mChannel =
+ new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
+
+ // Set the Notification Channel for the Notification Manager.
+ mNotificationManager.createNotificationChannel(mChannel);
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.i(TAG, "Service started");
+ boolean startedFromNotification = intent.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION,
+ false);
+
+ // We got here because the user decided to remove location updates from the notification.
+ if (startedFromNotification) {
+ removeLocationUpdates();
+ stopSelf();
+ }
+ // Tells the system to not try to recreate the service after it has been killed.
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mChangingConfiguration = true;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // Called when a client (MainActivity in case of this sample) comes to the foreground
+ // and binds with this service. The service should cease to be a foreground service
+ // when that happens.
+ Log.i(TAG, "in onBind()");
+ stopForeground(true);
+ mChangingConfiguration = false;
+ return mBinder;
+ }
+
+ @Override
+ public void onRebind(Intent intent) {
+ // Called when a client (MainActivity in case of this sample) returns to the foreground
+ // and binds once again with this service. The service should cease to be a foreground
+ // service when that happens.
+ Log.i(TAG, "in onRebind()");
+ stopForeground(true);
+ mChangingConfiguration = false;
+ super.onRebind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(TAG, "Last client unbound from service");
+
+ // Called when the last client (MainActivity in case of this sample) unbinds from this
+ // service. If this method is called due to a configuration change in MainActivity, we
+ // do nothing. Otherwise, we make this service a foreground service.
+ if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) {
+ Log.i(TAG, "Starting foreground service");
+
+ startForeground(NOTIFICATION_ID, getNotification());
+ }
+ return true; // Ensures onRebind() is called when a client re-binds.
+ }
+
+ @Override
+ public void onDestroy() {
+ mServiceHandler.removeCallbacksAndMessages(null);
+ }
+
+ /**
+ * 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");
+ Utils.setRequestingLocationUpdates(this, true);
+ startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
+ try {
+ mFusedLocationClient.requestLocationUpdates(mLocationRequest,
+ mLocationCallback, Looper.myLooper());
+ } catch (SecurityException unlikely) {
+ Utils.setRequestingLocationUpdates(this, false);
+ 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);
+ }
+ }
+
+ /**
+ * Returns the {@link NotificationCompat} used as part of the foreground service.
+ */
+ private Notification getNotification() {
+ Intent intent = new Intent(this, LocationUpdatesService.class);
+
+ CharSequence text = Utils.getLocationText(mLocation);
+
+ // Extra to help us figure out if we arrived in onStartCommand via the notification or not.
+ intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
+
+ // The PendingIntent that leads to a call to onStartCommand() in this service.
+ PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // The PendingIntent to launch activity.
+ PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, MainActivity.class), 0);
+
+ NotificationCompat.Builder builder;
+
+ // Builder initialization for Android O.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ builder = new NotificationCompat.Builder(
+ this, CHANNEL_ID
+ );
+ // Builder initialization for other versions of Android
+ } else {
+ builder = new NotificationCompat.Builder(
+ this, ""
+ );
+ }
+
+ builder.addAction(R.drawable.ic_launch, getString(R.string.launch_activity),
+ activityPendingIntent);
+ builder.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
+ servicePendingIntent);
+ builder.setContentText(text);
+ builder.setContentTitle(Utils.getLocationTitle(this));
+ builder.setOngoing(true);
+ builder.setPriority(NotificationCompat.PRIORITY_HIGH);
+ builder.setSmallIcon(R.mipmap.ic_launcher);
+ builder.setTicker(text);
+ builder.setWhen(System.currentTimeMillis());
+
+ return builder.build();
+ }
+
+ private void getLastLocation() {
+ try {
+ mFusedLocationClient.getLastLocation()
+ .addOnCompleteListener(task -> {
+ if (task.isSuccessful() && task.getResult() != null) {
+ mLocation = task.getResult();
+ } 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);
+
+ mLocation = location;
+
+ // Notify anyone listening for broadcasts about the new location.
+ Intent intent = new Intent(ACTION_BROADCAST);
+ intent.putExtra(EXTRA_LOCATION, location);
+ LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
+
+ // Update notification content if running as a foreground service.
+ if (serviceIsRunningInForeground(this)) {
+ mNotificationManager.notify(NOTIFICATION_ID, getNotification());
+ }
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Class used for the client Binder. Since this service runs in the same process as its
+ * clients, we don't need to deal with IPC.
+ */
+ public class LocalBinder extends Binder {
+ LocationUpdatesService getService() {
+ return LocationUpdatesService.this;
+ }
+ }
+
+ /**
+ * Returns true if this is a foreground service.
+ *
+ * @param context The {@link Context}.
+ */
+ public boolean serviceIsRunningInForeground(Context context) {
+ ActivityManager manager = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
+ Integer.MAX_VALUE)) {
+ if (getClass().getName().equals(service.service.getClassName())) {
+ if (service.foreground) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.kt b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.kt
new file mode 100644
index 00000000..6027f176
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.kt
@@ -0,0 +1,283 @@
+/**
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.gms.location.sample.locationupdatesforegroundservice
+
+import android.Manifest
+import android.content.*
+import android.content.pm.PackageManager
+import android.location.Location
+import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.provider.Settings
+import android.util.Log
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.ActivityCompat
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
+import androidx.preference.PreferenceManager
+import androidx.viewbinding.BuildConfig
+import com.google.android.gms.location.sample.locationupdatesforegroundservice.databinding.ActivityMainBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * The only activity in this sample.
+ *
+ * Note: Users have three options in "Q" regarding location:
+ *
+ * * Allow all the time
+ * * Allow while app is in use, i.e., while app is in foreground
+ * * Not allow location at all
+ *
+ * Because this app creates a foreground service (tied to a Notification) when the user navigates
+ * away from the app, it only needs location "while in use." That is, there is no need to ask for
+ * location all the time (which requires additional permissions in the manifest).
+ *
+ * "Q" also now requires developers to specify foreground service type in the manifest (in this
+ * case, "location").
+ *
+ * Note: For Foreground Services, "P" requires additional permission in manifest. Please check
+ * project manifest for more information.
+ *
+ * Note: for apps running in the background on "O" devices (regardless of the targetSdkVersion),
+ * location may be computed less frequently than requested when the app is not in the foreground.
+ * Apps that use a foreground service - which involves displaying a non-dismissable
+ * notification - can bypass the background location limits and request location updates as before.
+ *
+ * This sample uses a long-running bound and started service for location updates. The service is
+ * aware of foreground status of this activity, which is the only bound client in
+ * this sample. After requesting location updates, when the activity ceases to be in the foreground,
+ * the service promotes itself to a foreground service and continues receiving location updates.
+ * When the activity comes back to the foreground, the foreground service stops, and the
+ * notification associated with that foreground service is removed.
+ *
+ * While the foreground service notification is displayed, the user has the option to launch the
+ * activity from the notification. The user can also remove location updates directly from the
+ * notification. This dismisses the notification and stops the service.
+ */
+class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
+ companion object {
+ private val TAG = MainActivity::class.java.simpleName
+
+ // Used in checking for runtime permissions.
+ private const val REQUEST_PERMISSIONS_REQUEST_CODE = 34
+ }
+
+ // The BroadcastReceiver used to listen from broadcasts from the service.
+ private var myReceiver: MyReceiver? = null
+
+ // A reference to the service used to get location updates.
+ private var mService: LocationUpdatesService? = null
+
+ // Tracks the bound state of the service.
+ private var mBound = false
+
+ // UI elements.
+ private lateinit var mBinding: ActivityMainBinding
+
+ // Monitors the state of the connection to the service.
+ private val mServiceConnection: ServiceConnection = object : ServiceConnection {
+ override fun onServiceConnected(name: ComponentName, service: IBinder) {
+ val binder = service as LocationUpdatesService.LocalBinder
+ mService = binder.service
+ mBound = true
+ }
+
+ override fun onServiceDisconnected(name: ComponentName) {
+ mService = null
+ mBound = false
+ }
+ }
+
+ /**
+ * Receiver for broadcasts sent by [LocationUpdatesService].
+ */
+ private inner class MyReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION)
+ if (location != null) {
+ Toast.makeText(this@MainActivity, Utils.getLocationText(location),
+ Toast.LENGTH_SHORT).show()
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // ViewBinding initialization
+ mBinding = ActivityMainBinding.inflate(layoutInflater)
+ val view = mBinding.root
+ setContentView(view)
+ myReceiver = MyReceiver()
+
+ // Check that the user hasn't revoked permissions by going to Settings.
+ if (Utils.requestingLocationUpdates(this)) {
+ if (checkPermissions()) {
+ requestPermissions()
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .registerOnSharedPreferenceChangeListener(this)
+
+ mBinding.requestLocationUpdatesButton.setOnClickListener {
+ if (checkPermissions()) {
+ requestPermissions()
+ } else {
+ mService?.requestLocationUpdates()
+ }
+ }
+
+ mBinding.removeLocationUpdatesButton.setOnClickListener {
+ mService?.removeLocationUpdates()
+ }
+
+ // Restore the state of the buttons when the activity (re)launches.
+ setButtonsState(Utils.requestingLocationUpdates(this))
+
+ // Bind to the service. If the service is in foreground mode, this signals to the service
+ // that since this activity is in the foreground, the service can exit foreground mode.
+ bindService(Intent(this, LocationUpdatesService::class.java), mServiceConnection,
+ Context.BIND_AUTO_CREATE)
+ }
+
+ override fun onResume() {
+ super.onResume()
+ myReceiver?.let {
+ LocalBroadcastManager.getInstance(this)
+ .registerReceiver(it, IntentFilter(LocationUpdatesService.ACTION_BROADCAST))
+ }
+ }
+
+ override fun onPause() {
+ myReceiver?.let { LocalBroadcastManager.getInstance(this)
+ .unregisterReceiver(it) }
+ super.onPause()
+ }
+
+ override fun onStop() {
+ if (mBound) {
+ // Unbind from the service. This signals to the service that this activity is no longer
+ // in the foreground, and the service can respond by promoting itself to a foreground
+ // service.
+ unbindService(mServiceConnection)
+ mBound = false
+ }
+ PreferenceManager.getDefaultSharedPreferences(this)
+ .unregisterOnSharedPreferenceChangeListener(this)
+ super.onStop()
+ }
+
+ /**
+ * Returns the current state of the permissions needed.
+ */
+ private fun checkPermissions(): Boolean {
+ return PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_FINE_LOCATION)
+ }
+
+ private fun requestPermissions() {
+ val 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.")
+ Snackbar.make(
+ mBinding.root,
+ R.string.permission_rationale,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.ok) {
+ // Request permission
+ ActivityCompat.requestPermissions(this@MainActivity,
+ arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
+ REQUEST_PERMISSIONS_REQUEST_CODE)
+ }
+ .show()
+ } 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@MainActivity,
+ arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
+ REQUEST_PERMISSIONS_REQUEST_CODE)
+ }
+ }
+
+ /**
+ * Callback received when a permissions request has been completed.
+ */
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array,
+ grantResults: IntArray) {
+ Log.i(TAG, "onRequestPermissionResult")
+ if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
+ when {
+ grantResults.isEmpty() -> {
+ // If user interaction was interrupted, the permission request is cancelled and you
+ // receive empty arrays.
+ Log.i(TAG, "User interaction was cancelled.")
+ }
+ grantResults[0] == PackageManager.PERMISSION_GRANTED -> {
+ // Permission was granted.
+ mService?.requestLocationUpdates()
+ }
+ else -> {
+ // Permission denied.
+ setButtonsState(false)
+ Snackbar.make(
+ mBinding.root,
+ R.string.permission_denied_explanation,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.settings) {
+ // Build intent that displays the App settings screen.
+ val intent = Intent()
+ intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+ val uri = Uri.fromParts("package",
+ BuildConfig.LIBRARY_PACKAGE_NAME, null)
+ intent.data = uri
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ startActivity(intent)
+ }
+ .show()
+ }
+ }
+ }
+ }
+
+ override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, string: String) {
+ // Update the buttons state depending on whether location updates are being requested.
+ if (string == Utils.KEY_REQUESTING_LOCATION_UPDATES) {
+ setButtonsState(sharedPreferences.getBoolean(Utils.KEY_REQUESTING_LOCATION_UPDATES,
+ false))
+ }
+ }
+
+ private fun setButtonsState(requestingLocationUpdates: Boolean) {
+ if (requestingLocationUpdates) {
+ mBinding.requestLocationUpdatesButton.isEnabled = false
+ mBinding.removeLocationUpdatesButton.isEnabled = true
+ } else {
+ mBinding.requestLocationUpdatesButton.isEnabled = true
+ mBinding.removeLocationUpdatesButton.isEnabled = false
+ }
+ }
+}
\ No newline at end of file
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.kt b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.kt
new file mode 100644
index 00000000..50216589
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.kt
@@ -0,0 +1,65 @@
+/**
+ * Copyright 2017 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.gms.location.sample.locationupdatesforegroundservice
+
+import android.content.Context
+import android.location.Location
+import androidx.preference.PreferenceManager
+import java.text.DateFormat
+import java.util.*
+
+internal object Utils {
+ const val KEY_REQUESTING_LOCATION_UPDATES = "requesting_location_updates"
+
+ /**
+ * Returns true if requesting location updates, otherwise returns false.
+ *
+ * @param context The [Context].
+ */
+ @JvmStatic
+ fun requestingLocationUpdates(context: Context?): Boolean {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false)
+ }
+
+ /**
+ * Stores the location updates state in SharedPreferences.
+ * @param requestingLocationUpdates The location updates state.
+ */
+ @JvmStatic
+ fun setRequestingLocationUpdates(context: Context?, requestingLocationUpdates: Boolean) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(KEY_REQUESTING_LOCATION_UPDATES, requestingLocationUpdates)
+ .apply()
+ }
+
+ /**
+ * Returns the `location` object as a human readable string.
+ * @param location The [Location].
+ */
+ @JvmStatic
+ fun getLocationText(location: Location?): String {
+ return if (location == null) "Unknown location"
+ else "(${location.latitude}, ${location.longitude})"
+ }
+
+ @JvmStatic
+ fun getLocationTitle(context: Context): String {
+ return context.getString(R.string.location_updated,
+ DateFormat.getDateTimeInstance().format(Date()))
+ }
+}
\ No newline at end of file
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_cancel.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_cancel.png
new file mode 100644
index 00000000..4ccdcad5
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_cancel.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_launch.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_launch.png
new file mode 100644
index 00000000..93ba8835
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_launch.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_cancel.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_cancel.png
new file mode 100644
index 00000000..0b17795d
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_cancel.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_launch.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_launch.png
new file mode 100644
index 00000000..80821b15
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_launch.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_cancel.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_cancel.png
new file mode 100644
index 00000000..a4d0600c
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_cancel.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_launch.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_launch.png
new file mode 100644
index 00000000..59a9abf5
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_launch.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_cancel.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_cancel.png
new file mode 100644
index 00000000..17c25dac
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_cancel.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_launch.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_launch.png
new file mode 100644
index 00000000..268ef626
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_launch.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_cancel.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_cancel.png
new file mode 100644
index 00000000..65195d22
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_cancel.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_launch.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_launch.png
new file mode 100644
index 00000000..205f22d1
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_launch.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/layout/activity_main.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..e9975e45
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..c6677b37
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..13b22c57
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..f5590862
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b1a39f0f
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..7cd143a9
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values-w820dp/dimens.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 00000000..1e3d591f
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ 64dp
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/colors.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..92a17344
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/colors.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/dimens.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..86843c76
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/dimens.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ 16dp
+ 16dp
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/strings.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..093d2879
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,30 @@
+
+
+
+ Location Updates ForegroundService
+
+ Launch activity
+ Location unknown
+ Location Updates
+
+ Location permission is needed for core functionality
+ Permission was denied, but is needed for core
+ functionality.
+ Settings
+ OK
+ Request location updates
+ Remove location updates
+
+ Location Updated: %1$s
+
diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/styles.xml b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..839b93d9
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/styles.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/LocationUpdatesForegroundServiceKotlin/build.gradle b/LocationUpdatesForegroundServiceKotlin/build.gradle
new file mode 100644
index 00000000..1251469a
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/build.gradle
@@ -0,0 +1,31 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext {
+ // Android Gradle plugin
+ gradlePluginVersion = "4.0.1"
+ // Kotlin for Android | Kotlin Gradle plugin
+ kotlinVersion = '1.4.10'
+ }
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ // Android Gradle plugin
+ classpath "com.android.tools.build:gradle:$gradlePluginVersion"
+ // Kotlin Gradle plugin
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/LocationUpdatesForegroundServiceKotlin/gradle.properties b/LocationUpdatesForegroundServiceKotlin/gradle.properties
new file mode 100644
index 00000000..01236edf
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+
+android.useAndroidX=true
diff --git a/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.jar b/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..13372aef
Binary files /dev/null and b/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.properties b/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..d936ae58
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Oct 18 15:15:27 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/LocationUpdatesForegroundServiceKotlin/gradlew b/LocationUpdatesForegroundServiceKotlin/gradlew
new file mode 100644
index 00000000..9d82f789
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/LocationUpdatesForegroundServiceKotlin/gradlew.bat b/LocationUpdatesForegroundServiceKotlin/gradlew.bat
new file mode 100644
index 00000000..8a0b282a
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/LocationUpdatesForegroundServiceKotlin/settings.gradle b/LocationUpdatesForegroundServiceKotlin/settings.gradle
new file mode 100644
index 00000000..e7b4def4
--- /dev/null
+++ b/LocationUpdatesForegroundServiceKotlin/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/build.gradle b/build.gradle
index e38ca339..07bb13b7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,10 +1,15 @@
buildscript {
+ ext {
+ // Android Gradle plugin
+ gradlePluginVersion = '4.1.0'
+ }
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.3.2'
+ // Android Gradle plugin
+ classpath "com.android.tools.build:gradle:$gradlePluginVersion"
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a45563fc..51c80330 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Mar 14 09:02:58 PDT 2019
+#Sun Nov 01 19:11:59 MSK 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/settings.gradle b/settings.gradle
index e6d3f68a..00ce7c01 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,10 @@
-include ':ActivityRecognition:app', ':BasicLocationSample:java:app', ':BasicLocationSample:kotlin:app', ':Geofencing:app', ':LocationAddress:java:app', ':LocationAddress:kotlin:app', ':LocationUpdates:app', ':LocationUpdatesForegroundService:app', ':LocationUpdatesPendingIntent:app'
+include ':ActivityRecognition:app',
+ ':BasicLocation:app',
+ ':BasicLocationKotlin:app',
+ ':Geofencing:app',
+ ':LocationAddress:java:app',
+ ':LocationAddress:kotlin:app',
+ ':LocationUpdates:app',
+ ':LocationUpdatesForegroundService:app',
+ ":LocationUpdatesForegroundServiceKotlin:app"
+ ':LocationUpdatesPendingIntent:app'