From 9f1cc36145a5615ee7ab92edc3d1acee15b9b11b Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Thu, 17 Sep 2020 12:56:32 +0300 Subject: [PATCH 01/15] Project: -updated Android Gradle plugin to 4.0.1 -updated Gradle to 6.1.1 LocationUpdatesForegroundService: -updated Android Gradle plugin to 4.0.1 -reorganised dependencies and added "ext" block -updated AppCompat to 1.2.0 -updated Espresso to 3.3.0 -updated Junit to 4.13 -updated MaterialComponents to 1.2.1 --- ActivityRecognition/app/build.gradle | 2 +- .../app/build.gradle | 39 ++++++++++++++++--- LocationUpdatesForegroundService/build.gradle | 10 +++-- build.gradle | 7 +++- gradle/wrapper/gradle-wrapper.properties | 4 +- 5 files changed, 48 insertions(+), 14 deletions(-) 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/LocationUpdatesForegroundService/app/build.gradle b/LocationUpdatesForegroundService/app/build.gradle index 82ccdfff..47d2e0f7 100644 --- a/LocationUpdatesForegroundService/app/build.gradle +++ b/LocationUpdatesForegroundService/app/build.gradle @@ -19,14 +19,41 @@ android { } } +ext { + // Jetpack + // AppCompat + appCompatVersion = "1.2.0" + // 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']) - androidTestImplementation('androidx.test.espresso:espresso-core:3.1.1', { + + // Jetpack + // AppCompat + implementation "androidx.appcompat:appcompat:$appCompatVersion" + // MaterialComponents + implementation "com.google.android.material:material:$materialComponentsVersion" + + // Google + implementation "com.google.android.gms:play-services-location:$locationVersion" + + // Testing + // Junit + testImplementation "junit:junit:$junitVersion" + // Espresso + androidTestImplementation("androidx.test.espresso:espresso-core:$espressoVersion", { exclude group: 'com.android.support', module: 'support-annotations' }) - implementation 'androidx.appcompat:appcompat:1.1.0' - testImplementation 'junit:junit:4.12' - - implementation 'com.google.android.gms:play-services-location:17.0.0' - implementation 'com.google.android.material:material:1.0.0' } 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/build.gradle b/build.gradle index e38ca339..831fc88b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,15 @@ buildscript { + ext { + // Android Gradle plugin + gradlePluginVersion = '4.0.1' + } 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..b7eef589 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 +#Thu Sep 17 12:17:00 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.1.1-all.zip From 41c7bbc3602553490f6d5c8cef6fcfd56d5b0842 Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Thu, 17 Sep 2020 13:20:19 +0300 Subject: [PATCH 02/15] LocationUpdatesForegroundService: -reorganised .gitignore file -enabled ViewBinding -MainActivity - refractored code for ViewBinding usage -added LocalBroadcastManager dependency -MainActivity - replaced BuildConfig.APPLICATION_ID for BuildConfig.LIBRARY_PACKAGE_NAME as BuildConfig.APPLICATION_ID became deprecated -updated imports --- LocationUpdatesForegroundService/.gitignore | 5 +- .../app/build.gradle | 5 ++ .../LocationUpdatesService.java | 3 +- .../MainActivity.java | 53 +++++++++---------- 4 files changed, 37 insertions(+), 29 deletions(-) diff --git a/LocationUpdatesForegroundService/.gitignore b/LocationUpdatesForegroundService/.gitignore index 73145024..e3ee5f5f 100644 --- a/LocationUpdatesForegroundService/.gitignore +++ b/LocationUpdatesForegroundService/.gitignore @@ -1,6 +1,9 @@ +# Directories build/ .idea .gradle + +# Files *.iml local.properties -.DS_Store +.DS_Store \ No newline at end of file diff --git a/LocationUpdatesForegroundService/app/build.gradle b/LocationUpdatesForegroundService/app/build.gradle index 47d2e0f7..8041cd6e 100644 --- a/LocationUpdatesForegroundService/app/build.gradle +++ b/LocationUpdatesForegroundService/app/build.gradle @@ -17,6 +17,9 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + buildFeatures { + viewBinding true + } } ext { @@ -43,6 +46,8 @@ dependencies { // Jetpack // AppCompat implementation "androidx.appcompat:appcompat:$appCompatVersion" + // LocalBroadcastManager + implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' // MaterialComponents implementation "com.google.android.material:material:$materialComponentsVersion" diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java index 13c7ec0c..d60c4195 100644 --- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java +++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java @@ -32,10 +32,11 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import android.util.Log; import com.google.android.gms.location.FusedLocationProviderClient; import com.google.android.gms.location.LocationCallback; 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..f1160cef 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.net.Uri; +import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.appcompat.app.AppCompatActivity; -import android.os.Bundle; +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.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,8 +118,13 @@ 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)) { @@ -138,10 +140,7 @@ 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() { + mBinding.requestLocationUpdatesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (!checkPermissions()) { @@ -152,7 +151,7 @@ public void onClick(View view) { } }); - mRemoveLocationUpdatesButton.setOnClickListener(new View.OnClickListener() { + mBinding.removeLocationUpdatesButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mService.removeLocationUpdates(); @@ -267,7 +266,7 @@ public void onClick(View view) { intent.setAction( Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", - BuildConfig.APPLICATION_ID, null); + BuildConfig.LIBRARY_PACKAGE_NAME, null); intent.setData(uri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); @@ -303,11 +302,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); } } } From bce5adbd84b0036e31fdf0b0c59fed9fa4f16398 Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Thu, 17 Sep 2020 13:55:24 +0300 Subject: [PATCH 03/15] LocationUpdatesForegroundService: -reorganised LocalBroadcastManager in build.gradle -MainActivity - inverted checkPermissions method -set Java sourceCompatibility and targetCompatibility versions to 1.8 -refractored code for Java 1.8 --- .../app/build.gradle | 8 ++- .../LocationUpdatesService.java | 16 ++--- .../MainActivity.java | 63 ++++++++----------- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/LocationUpdatesForegroundService/app/build.gradle b/LocationUpdatesForegroundService/app/build.gradle index 8041cd6e..c711f6da 100644 --- a/LocationUpdatesForegroundService/app/build.gradle +++ b/LocationUpdatesForegroundService/app/build.gradle @@ -17,6 +17,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility = 1.8 + targetCompatibility = 1.8 + } buildFeatures { viewBinding true } @@ -26,6 +30,8 @@ ext { // Jetpack // AppCompat appCompatVersion = "1.2.0" + // LocalBroadcastManager + localBroadcastManager = "1.0.0" // MaterialComponents materialComponentsVersion = "1.2.1" @@ -47,7 +53,7 @@ dependencies { // AppCompat implementation "androidx.appcompat:appcompat:$appCompatVersion" // LocalBroadcastManager - implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' + implementation "androidx.localbroadcastmanager:localbroadcastmanager:$localBroadcastManager" // MaterialComponents implementation "com.google.android.material:material:$materialComponentsVersion" diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java index d60c4195..c0a11052 100644 --- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java +++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java @@ -34,7 +34,6 @@ import android.os.Looper; import android.util.Log; -import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.localbroadcastmanager.content.LocalBroadcastManager; @@ -43,8 +42,6 @@ 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; /** * A bound and started service that is promoted to a foreground service when location updates have @@ -302,14 +299,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 f1160cef..afd6e3ee 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 @@ -128,7 +128,7 @@ protected void onCreate(Bundle savedInstanceState) { // Check that the user hasn't revoked permissions by going to Settings. if (Utils.requestingLocationUpdates(this)) { - if (!checkPermissions()) { + if (checkPermissions()) { requestPermissions(); } } @@ -140,23 +140,16 @@ protected void onStart() { PreferenceManager.getDefaultSharedPreferences(this) .registerOnSharedPreferenceChangeListener(this); - mBinding.requestLocationUpdatesButton.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(); } }); - mBinding.removeLocationUpdatesButton.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)); @@ -198,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() { @@ -215,14 +208,11 @@ private void requestPermissions() { findViewById(R.id.activity_main), 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 { @@ -258,19 +248,16 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis findViewById(R.id.activity_main), 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.LIBRARY_PACKAGE_NAME, 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(); } From eb50e6c6706004571f650a6e1e233d9f61a098f6 Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Thu, 17 Sep 2020 17:14:06 +0300 Subject: [PATCH 04/15] LocationUpdatesForegroundService: -LocationUpdatesService - removed deprecated FusedLocationProviderApi in documentation and replaced it with FusedLocationProviderClient -LocationUpdatesService - replaced NotificationCompat.Builder constructor with non-deprecated one, refractored NotificationCompat.Builder initialization code -Added Jetpack Preference dependency -MainActivity and Utils classes - refractored to use Jetpack's PreferenceManager instead of deprecated one --- .../app/build.gradle | 8 +++- .../LocationUpdatesService.java | 42 +++++++++++-------- .../MainActivity.java | 2 +- .../Utils.java | 3 +- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/LocationUpdatesForegroundService/app/build.gradle b/LocationUpdatesForegroundService/app/build.gradle index c711f6da..0cbaeb82 100644 --- a/LocationUpdatesForegroundService/app/build.gradle +++ b/LocationUpdatesForegroundService/app/build.gradle @@ -31,7 +31,9 @@ ext { // AppCompat appCompatVersion = "1.2.0" // LocalBroadcastManager - localBroadcastManager = "1.0.0" + localBroadcastManagerVersion = "1.0.0" + // Preference + preferenceVersion = "1.1.1" // MaterialComponents materialComponentsVersion = "1.2.1" @@ -53,7 +55,9 @@ dependencies { // AppCompat implementation "androidx.appcompat:appcompat:$appCompatVersion" // LocalBroadcastManager - implementation "androidx.localbroadcastmanager:localbroadcastmanager:$localBroadcastManager" + implementation "androidx.localbroadcastmanager:localbroadcastmanager:$localBroadcastManagerVersion" + // Preference + implementation "androidx.preference:preference:$preferenceVersion" // MaterialComponents implementation "com.google.android.material:material:$materialComponentsVersion" diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java index c0a11052..10083a25 100644 --- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java +++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java @@ -104,7 +104,7 @@ public class LocationUpdatesService extends Service { private NotificationManager mNotificationManager; /** - * Contains parameters used by {@link com.google.android.gms.location.FusedLocationProviderApi}. + * Contains parameters used by {@link com.google.android.gms.location.FusedLocationProviderClient}. */ private LocationRequest mLocationRequest; @@ -274,24 +274,32 @@ private Notification getNotification() { // The PendingIntent to launch activity. PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(this) - .addAction(R.drawable.ic_launch, getString(R.string.launch_activity), - activityPendingIntent) - .addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates), - servicePendingIntent) - .setContentText(text) - .setContentTitle(Utils.getLocationTitle(this)) - .setOngoing(true) - .setPriority(Notification.PRIORITY_HIGH) - .setSmallIcon(R.mipmap.ic_launcher) - .setTicker(text) - .setWhen(System.currentTimeMillis()); - - // Set the Channel ID for Android O. + + NotificationCompat.Builder builder; + + // Builder initialization for Android O. if (Build.VERSION.SDK_INT >= 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(Notification.PRIORITY_HIGH); + builder.setSmallIcon(R.mipmap.ic_launcher); + builder.setTicker(text); + builder.setWhen(System.currentTimeMillis()); return builder.build(); } 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 afd6e3ee..b28e2e8d 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 @@ -29,7 +29,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; -import android.preference.PreferenceManager; import android.provider.Settings; import android.util.Log; import android.view.View; @@ -39,6 +38,7 @@ 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; 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; From a97df6f2153d6554cd40d6f0040f10d71ed8131f Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Fri, 18 Sep 2020 09:48:16 +0300 Subject: [PATCH 05/15] LocationUpdatesForegroundService module: -updated Gradle version to 6.1.1 -LocationUpdatesService - replaced deprecated Notification.PRIORITY_HIGH with NotificationCompat.PRIORITY_HIGH --- .../LocationUpdatesService.java | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java index 10083a25..7ae1c534 100644 --- a/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java +++ b/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java @@ -296,7 +296,7 @@ private Notification getNotification() { builder.setContentText(text); builder.setContentTitle(Utils.getLocationTitle(this)); builder.setOngoing(true); - builder.setPriority(Notification.PRIORITY_HIGH); + builder.setPriority(NotificationCompat.PRIORITY_HIGH); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setTicker(text); builder.setWhen(System.currentTimeMillis()); 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 From 00732690bae5fc9576cd0e6dbdc18fc6cbe1309c Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Fri, 18 Sep 2020 10:10:33 +0300 Subject: [PATCH 06/15] LocationUpdatesForegroundService module: -added android.useAndroidX=true to gradle.properties --- LocationUpdatesForegroundService/gradle.properties | 2 ++ 1 file changed, 2 insertions(+) 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 From 6c4a216e39b2dd5d9310e133cdea8f0537ab691b Mon Sep 17 00:00:00 2001 From: MadRat96 Date: Sun, 20 Sep 2020 07:55:40 +0300 Subject: [PATCH 07/15] LocationUpdatesForegroundService module: -replaced remaining findViewById usages with ViewBinding LocationUpdatesForegroundServiceKotlin module: -initial commit --- .../MainActivity.java | 8 +- .../.gitignore | 9 + .../.google/packaging.yaml | 25 ++ .../app/.gitignore | 1 + .../app/build.gradle | 74 ++++ .../app/proguard-rules.pro | 17 + .../app/src/main/AndroidManifest.xml | 45 +++ .../LocationUpdatesService.java | 376 ++++++++++++++++++ .../MainActivity.java | 299 ++++++++++++++ .../Utils.java | 66 +++ .../src/main/res/drawable-hdpi/ic_cancel.png | Bin 0 -> 564 bytes .../src/main/res/drawable-hdpi/ic_launch.png | Bin 0 -> 291 bytes .../src/main/res/drawable-mdpi/ic_cancel.png | Bin 0 -> 431 bytes .../src/main/res/drawable-mdpi/ic_launch.png | Bin 0 -> 199 bytes .../src/main/res/drawable-xhdpi/ic_cancel.png | Bin 0 -> 745 bytes .../src/main/res/drawable-xhdpi/ic_launch.png | Bin 0 -> 295 bytes .../main/res/drawable-xxhdpi/ic_cancel.png | Bin 0 -> 991 bytes .../main/res/drawable-xxhdpi/ic_launch.png | Bin 0 -> 398 bytes .../main/res/drawable-xxxhdpi/ic_cancel.png | Bin 0 -> 1296 bytes .../main/res/drawable-xxxhdpi/ic_launch.png | Bin 0 -> 480 bytes .../app/src/main/res/layout/activity_main.xml | 39 ++ .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3200 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1914 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4055 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 6689 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9604 bytes .../app/src/main/res/values-w820dp/dimens.xml | 19 + .../app/src/main/res/values/colors.xml | 19 + .../app/src/main/res/values/dimens.xml | 18 + .../app/src/main/res/values/strings.xml | 30 ++ .../app/src/main/res/values/styles.xml | 24 ++ .../build.gradle | 27 ++ .../gradle.properties | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../gradlew | 160 ++++++++ .../gradlew.bat | 90 +++++ .../settings.gradle | 1 + settings.gradle | 11 +- 39 files changed, 1378 insertions(+), 5 deletions(-) create mode 100644 LocationUpdatesForegroundServiceKotlin/.gitignore create mode 100644 LocationUpdatesForegroundServiceKotlin/.google/packaging.yaml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/.gitignore create mode 100644 LocationUpdatesForegroundServiceKotlin/app/build.gradle create mode 100644 LocationUpdatesForegroundServiceKotlin/app/proguard-rules.pro create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/AndroidManifest.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_cancel.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-hdpi/ic_launch.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_cancel.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-mdpi/ic_launch.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_cancel.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xhdpi/ic_launch.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_cancel.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxhdpi/ic_launch.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_cancel.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/drawable-xxxhdpi/ic_launch.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/layout/activity_main.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/values-w820dp/dimens.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/colors.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/dimens.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/strings.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/app/src/main/res/values/styles.xml create mode 100644 LocationUpdatesForegroundServiceKotlin/build.gradle create mode 100644 LocationUpdatesForegroundServiceKotlin/gradle.properties create mode 100644 LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.jar create mode 100644 LocationUpdatesForegroundServiceKotlin/gradle/wrapper/gradle-wrapper.properties create mode 100644 LocationUpdatesForegroundServiceKotlin/gradlew create mode 100644 LocationUpdatesForegroundServiceKotlin/gradlew.bat create mode 100644 LocationUpdatesForegroundServiceKotlin/settings.gradle 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 b28e2e8d..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 @@ -205,7 +205,7 @@ 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, view -> { @@ -245,7 +245,7 @@ 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, view -> { @@ -279,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)); } 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..0cbaeb82 --- /dev/null +++ b/LocationUpdatesForegroundServiceKotlin/app/build.gradle @@ -0,0 +1,74 @@ +apply plugin: 'com.android.application' + +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:$preferenceVersion" + // MaterialComponents + implementation "com.google.android.material:material:$materialComponentsVersion" + + // Google + implementation "com.google.android.gms:play-services-location:$locationVersion" + + // 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..7ae1c534 --- /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.java b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java new file mode 100644 index 00000000..3d4e5f91 --- /dev/null +++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/MainActivity.java @@ -0,0 +1,299 @@ +/** + * 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.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.SharedPreferences; +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.view.View; +import android.widget.Toast; + +import androidx.annotation.NonNull; +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. + */ +public class MainActivity extends AppCompatActivity implements + SharedPreferences.OnSharedPreferenceChangeListener { + private static final String TAG = MainActivity.class.getSimpleName(); + + // Used in checking for runtime permissions. + private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; + + // The BroadcastReceiver used to listen from broadcasts from the service. + private MyReceiver myReceiver; + + // A reference to the service used to get location updates. + private LocationUpdatesService mService = null; + + // Tracks the bound state of the service. + private boolean mBound = false; + + // UI elements. + private ActivityMainBinding mBinding; + + // Monitors the state of the connection to the service. + private final ServiceConnection mServiceConnection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service; + mService = binder.getService(); + mBound = true; + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + mBound = false; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // ViewBinding initialization + mBinding = ActivityMainBinding.inflate(getLayoutInflater()); + View view = mBinding.getRoot(); + setContentView(view); + + myReceiver = new MyReceiver(); + + // Check that the user hasn't revoked permissions by going to Settings. + if (Utils.requestingLocationUpdates(this)) { + if (checkPermissions()) { + requestPermissions(); + } + } + } + + @Override + protected void onStart() { + super.onStart(); + PreferenceManager.getDefaultSharedPreferences(this) + .registerOnSharedPreferenceChangeListener(this); + + mBinding.requestLocationUpdatesButton.setOnClickListener(view -> { + if (checkPermissions()) { + requestPermissions(); + } else { + mService.requestLocationUpdates(); + } + }); + + mBinding.removeLocationUpdatesButton.setOnClickListener( + view -> 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(new Intent(this, LocationUpdatesService.class), mServiceConnection, + Context.BIND_AUTO_CREATE); + } + + @Override + protected void onResume() { + super.onResume(); + LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver, + new IntentFilter(LocationUpdatesService.ACTION_BROADCAST)); + } + + @Override + protected void onPause() { + LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver); + super.onPause(); + } + + @Override + protected void 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 boolean checkPermissions() { + return PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_FINE_LOCATION); + } + + 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."); + Snackbar.make( + mBinding.getRoot(), + R.string.permission_rationale, + Snackbar.LENGTH_INDEFINITE) + .setAction(R.string.ok, view -> { + // Request permission + ActivityCompat.requestPermissions(MainActivity.this, + new String[]{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(MainActivity.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"); + 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."); + } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Permission was granted. + mService.requestLocationUpdates(); + } else { + // Permission denied. + setButtonsState(false); + Snackbar.make( + mBinding.getRoot(), + R.string.permission_denied_explanation, + Snackbar.LENGTH_INDEFINITE) + .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(); + } + } + } + + /** + * Receiver for broadcasts sent by {@link LocationUpdatesService}. + */ + private class MyReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION); + if (location != null) { + Toast.makeText(MainActivity.this, Utils.getLocationText(location), + Toast.LENGTH_SHORT).show(); + } + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String string) { + // Update the buttons state depending on whether location updates are being requested. + if (string.equals(Utils.KEY_REQUESTING_LOCATION_UPDATES)) { + setButtonsState(sharedPreferences.getBoolean(Utils.KEY_REQUESTING_LOCATION_UPDATES, + false)); + } + } + + private void setButtonsState(boolean requestingLocationUpdates) { + if (requestingLocationUpdates) { + mBinding.requestLocationUpdatesButton.setEnabled(false); + mBinding.removeLocationUpdatesButton.setEnabled(true); + } else { + mBinding.requestLocationUpdatesButton.setEnabled(true); + mBinding.removeLocationUpdatesButton.setEnabled(false); + } + } +} diff --git a/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java new file mode 100644 index 00000000..df98f6ca --- /dev/null +++ b/LocationUpdatesForegroundServiceKotlin/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/Utils.java @@ -0,0 +1,66 @@ +/** + * 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.Date; + +class Utils { + + static final String KEY_REQUESTING_LOCATION_UPDATES = "requesting_location_updates"; + + /** + * Returns true if requesting location updates, otherwise returns false. + * + * @param context The {@link Context}. + */ + static boolean requestingLocationUpdates(Context context) { + return PreferenceManager.getDefaultSharedPreferences(context) + .getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false); + } + + /** + * Stores the location updates state in SharedPreferences. + * @param requestingLocationUpdates The location updates state. + */ + static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) { + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putBoolean(KEY_REQUESTING_LOCATION_UPDATES, requestingLocationUpdates) + .apply(); + } + + /** + * Returns the {@code location} object as a human readable string. + * @param location The {@link Location}. + */ + static String getLocationText(Location location) { + return location == null ? "Unknown location" : + "(" + location.getLatitude() + ", " + location.getLongitude() + ")"; + } + + static String getLocationTitle(Context context) { + return context.getString(R.string.location_updated, + DateFormat.getDateTimeInstance().format(new Date())); + } +} 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 0000000000000000000000000000000000000000..4ccdcad58e8d601141f3c7ee1aadd5856c7d25d3 GIT binary patch literal 564 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k3?#4J%UA`Z<^=eJxH`3d8MJ8nil_>pJB>?% z{DK+S{HC0E_3QX#!REJb8U>m+J)L>O+8Hqi{~emen3^=FA%)dDdE(KZO!MlV z^M$Tk9i}p2HvxG`I?6b=9Y literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..93ba8835969d2dab237536d9c8760b81e5147e85 GIT binary patch literal 291 zcmV+;0o?wHP)m+-DM1_~1aXWGWSDCTMsSA+ zMhD^;DTrhApe5=FMt)=~)Dw*SQZEQb39>S1E}$Lx$RAr5z2B#RwrCJlrGN_53NSZR pP>`YQS9;<+stiLw$};FLKu;w^2}+J6CBgsz002ovPDHLkV1fo?amxSz literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..0b17795da74effb4110b8739f336229fe22cf981 GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM3?#3wJbMaARR{QlxH`3d87LGf{LvQZ42_Z? zzhDOTxOESIt&NFz`Xr2L`jpU1j;~%`dCaDx)PX+!kw65MT&*)-~n!4fuHN=&k?%4<$7B$jv=I^YA8y*_?-DyOqS$8{dCt z-?Bt<%@g-O3r`$;xJUcG<&Wbh?`1!mUzr(lRNv}KlT!u{`%3St4%t=iiRn(7j-j`{ zmlP~GFrka-sZF1Vl8P4VxyMgh*&M1GE$@4Wr*UokteTr}XsVOb>nwli0^vN7rjYjP zZ22D-=5Z<*{Yhr@e)wD6qG($8Oo>O-x#)I4P5<{-LPi>1vg4^Zku=&c9(%7t)!yIk}BpFPx=7 zasE9^Q${^|7Dlc2Qy8bb_snZznCQ(E)Ayml>l|~;y+^e##TV8v?^L-^u=5$K_Nl3i yPoB@PYq%n`V{XKWqfC>9xt=O>pIB-Cf$74AFYE8#WSIeU8H1;*pUXO@geCw+ph`3V literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a4d0600cb5656937eebf6e92f82e08cf242c3c13 GIT binary patch literal 745 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sHv)V@T%B6K3`=ygoZ&9ekM1Qw ze!&b(mR0Mny!};aA=mZvNr$Z1?4L74V&6rw&Sl}+v2{+w`!~XWqE{@L7S6m}I;MTu zBSy2O6{|13E;oJH8Q1nhYVs?=&?%caWdi@r>tPB{%fc8%`+(vbqs}0PRYEvEp^!?N#(Hf z&-4Nm>mxg^tnV^oE4UN1BYvM7?=<$RygWme?TbFIE|>6HyQf{^ib5_(J##96Cx=a`fjm*W9yy= zc7B>0CNB6`AV2p%`?O=rd0+LV6vcDI{m5DKsxGs6*T!W#`%iRBvPwQ%ZLR)HM56ND z=OjahN3m?FpE8@hCD%M~Y1EsbUK_dP32Rio!$IENi@aYx77OtbJN98?vwcP3K{=)! z7yqfVth}=5Pcws?=^~4o#6syUWkENl8zwYynk-|pJb_%&t%?S?Y` le^x&B`Fk?QuA%q|`(AxrpQ}dO&jaI?!PC{xWt~$(696BuW+ngt literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..59a9abf5deb31e6a3530ea188f8df4df27a388f2 GIT binary patch literal 295 zcmV+?0oeYDP)YytubH zPMz&+8*IP06Wy#2MX%3ZSf$l)S!=WTzsI=UT`S+i0q`t!$djyW^f1rF`!lel#L z!166YKLU0*d2jc!5B%*}!1I&KwPOCg*PI9Q{lzxhE%^{UWe*$wg>$?s90W^?gcul@ z^gUf1Ln`9lUW*OB>>zUd;bn*03_%{=hE7X$dk<I_vW(_r1@ronCbAOwPW5lDkQY_U8L1%=W9?8c`%!G<}+F_Oh7Bhdp7d zR)?K=ZIk`^^szIuO~s#(4QoN^{gfmFKZ1+;aC$pZks7?9F2JI)1B7 zDmB5zDc#$?e%^92Z0nZS=cbvO{4O{wwkrMo8Pjd=f}j0#N=P>kYRq_V?Dc!eGx(%%bUd04pVV~V+pMy%`76V$#Jx{kCLOMANRw=S zXff$<^ObGWR8A(!FJR)H^x}iS0woZ|p)j4*v&7;j{{?%#vsIgt6F0q`W^Wep&+?>Y zhog}BlRZyd3Ra(RPjM{zz;e3X?73H;ifTHGE@z*{T;Xjx78;fsu>#lHPXE`o>Ue+f zsr|B$4<}^U9yDBOIsR?ZrL~7{XdRr~EM!&V>SgBMBBvm2|Ig}+oF&(8F1|5b+=P))p?$8k=^w2yPvuD%ec6Sx%UcZESNMWz9(~)U-jQc odl}V}887&M?KQ4S+xvz6N}k5$4DnBgfvJxI*rE0~^zWo(u(sa|hV7`aehXGqW>2*wDyek)y!CC(Y4t7$^a{>dCTGcI#S80HFUZuEwtSZBjsBzhd9(1G?rHg9rFVdQ&MBb@0C=>RGXMYp literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..65195d228d5ce3944d34eba103e44423d48106bc GIT binary patch literal 1296 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wF4y>V*-3aT%B6Kj3OF^0QW4u=fL1D zE(!7rW{~i%n7{wV>mPsj%@gMdnRD;X>>zHYHQ&Cj^5iRd_@Icl=Kb4BuGpuKqm=gU zQb_##k#iBpnG<4a%GxiUX{tz`TsSxGli~-rWA4@F{FY_!EZ!6_-(PIQsB$S2ioF zE9278kUJB!$^x@RMWS-A?t1?_>TTJ+|33Pq&;F|K@%+&DT?zYO`D9icAZ-F zYF1QyY;En__1hM1T(yaF>E%DGj`}NJxFzPo*`jsZIz#c!P0=Rvi5WfHc2z$r(Ess^ zxBJyw?*mK!F|OiKdwA@E!Bsc6hc_a+_WUE~yO!F^h%~OO_emuO|I2yF z+g+OaQu66@@!ziw>4?}JeK9M(Q0CI)F9~@}M{Aa;f7VFqk<&eJEuc#-wRnTE&w00m z#q(7sv@CtV(0X(!kM2|MNUmnih@VL}{&%IGcCzc6TF7-jLGjbwE&a=z?y!fYCa#rC z3;b*+a5CcUZ0^?vzXXln+lxNgw{wGFljqVK5lk+QZ`a=O~XMdZl7j{>=e z7VK=^)~fs0ZoBY=xQV-(?o2OJ*cuQR@UB>V(tNk*^IVm0?Y=97OqkVl$NaYWoF%K8 zisrK`1-N?ue9n5;j^A~`tW{1i+s}syu2{Hk!R_Z0D{6IfxeL>O+lVpBi?V*T^LJU$ z*VR-tKT&YS@SObppC-K)w!YFPzkeN?Sy8(*#)m)kX1QVVfQ`{+DvIox-|Nf1kt7^U^bp zR`asncGvj4PoIOkc1mj!e`6$9V1y^nv`2U|jC$;uuoF`1X#Wmv*2;+ryXZ z9z{J7+QGH$-C0qUq%Iw&4dR7@f}1&?CQUTCxZg(UFvp>v9Cr3KQ_udeGthEkdp@W5 zoF#_wR>>6BZ7=Hd>`1CQJG3D`m<`c){8CV`Oa~xwA z=;2q8kasY!Ye=YJIQWBM(?>>@NAs_;K~yukJYwcJ!mq=iP$%Q?$EM*&CBw(h42?hZ zFMniAdBiMW&p)C6J^#iUu;EG|tw81M0v-Gdod49@_j4-9%R3m^H5Ak^l;8Zp;Fra} z^9MszOF#{Su9JpcgP5YAyhC$=bQk{uSzzFyf&*9Bx8K^veqi&3#nImxb_iCLe3oOV z&#vCMKPTS$ErY=)!Ef7E@a|>kv-`_^@m+R(72_6;(?4Eo{J#~si-Naj-9>z{U4PEMo>gaFfgz&fSB!!3G#2!TUj?hsyYC&+0)g}Wt~$(697Iax|{$2 literal 0 HcmV?d00001 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 @@ + + + + + + +