WIP #571
This commit is contained in:
parent
7ebaf736b5
commit
2ec549af7e
@ -59,7 +59,7 @@
|
||||
<service
|
||||
android:name=".LocationUpdatesService"
|
||||
android:enabled="true"
|
||||
android:exported="true" />
|
||||
android:exported="false" />
|
||||
<receiver android:name=".NotificationActionReceiver" android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
@ -72,6 +72,11 @@
|
||||
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver android:name=".Autostart">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service
|
||||
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.keyboardcrumbs.hassclient;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.IBinder;
|
||||
|
||||
public class Autostart extends BroadcastReceiver {
|
||||
|
||||
private PendingResult result;
|
||||
|
||||
private final ServiceConnection mServiceConnection = new ServiceConnection() {
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
|
||||
binder.getService().requestLocationUpdates();
|
||||
result.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName componentName) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (/*Utils.requestingLocationUpdates(context) && */Intent.ACTION_BOOT_COMPLETED.equalsIgnoreCase(intent.getAction())) {
|
||||
context.getApplicationContext().bindService(new Intent(context, LocationUpdatesService.class), mServiceConnection,
|
||||
Context.BIND_AUTO_CREATE);
|
||||
result = goAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,14 +1,11 @@
|
||||
package com.keyboardcrumbs.hassclient;
|
||||
|
||||
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;
|
||||
@ -16,8 +13,9 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.work.BackoffPolicy;
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.Data;
|
||||
@ -36,20 +34,6 @@ import com.google.android.gms.location.LocationServices;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 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 assocaited with that service is removed.
|
||||
*/
|
||||
public class LocationUpdatesService extends Service {
|
||||
|
||||
private static final String PACKAGE_NAME =
|
||||
@ -59,47 +43,23 @@ public class LocationUpdatesService extends Service {
|
||||
|
||||
private static final String CHANNEL_ID = "location_service";
|
||||
|
||||
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 identifier for the notification displayed for the foreground service.
|
||||
*/
|
||||
private static final int NOTIFICATION_ID = 954311;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
private LocationRequest mLocationRequest;
|
||||
|
||||
private long requestInterval = 90000;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
@ -117,7 +77,9 @@ public class LocationUpdatesService extends Service {
|
||||
}
|
||||
};
|
||||
|
||||
createLocationRequest();
|
||||
mLocationRequest = new LocationRequest();
|
||||
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
|
||||
|
||||
getLastLocation();
|
||||
|
||||
HandlerThread handlerThread = new HandlerThread(TAG);
|
||||
@ -125,14 +87,11 @@ public class LocationUpdatesService extends Service {
|
||||
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 = "Location updates";
|
||||
// Create the channel for the notification
|
||||
NotificationChannel mChannel =
|
||||
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
|
||||
|
||||
// Set the Notification Channel for the Notification Manager.
|
||||
mNotificationManager.createNotificationChannel(mChannel);
|
||||
}
|
||||
}
|
||||
@ -148,49 +107,24 @@ public class LocationUpdatesService extends Service {
|
||||
removeLocationUpdates();
|
||||
stopSelf();
|
||||
}
|
||||
// Tells the system to not try to recreate the service after it has been killed.
|
||||
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
mChangingConfiguration = true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
// Called when MainActivity 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 MainActivity 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 ) 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)) {
|
||||
startForeground(NOTIFICATION_ID, getNotification());
|
||||
}
|
||||
return true; // Ensures onRebind() is called when a client re-binds.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -199,16 +133,19 @@ public class LocationUpdatesService extends Service {
|
||||
}
|
||||
|
||||
public void requestLocationUpdates() {
|
||||
requestInterval = Utils.getLocationUpdateIntervals(getApplicationContext());
|
||||
long requestInterval = Utils.getLocationUpdateIntervals(getApplicationContext());
|
||||
Log.i(TAG, "Requesting location updates. Interval is " + requestInterval);
|
||||
mLocationRequest.setInterval(requestInterval);
|
||||
mLocationRequest.setFastestInterval(requestInterval);
|
||||
Utils.setRequestingLocationUpdates(this, true);
|
||||
startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
|
||||
startForeground(NOTIFICATION_ID, getNotification());
|
||||
try {
|
||||
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
|
||||
mLocationCallback, Looper.myLooper());
|
||||
} catch (SecurityException unlikely) {
|
||||
//When we lost permission
|
||||
Utils.setRequestingLocationUpdates(this, false);
|
||||
removeLocationUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,30 +153,24 @@ public class LocationUpdatesService extends Service {
|
||||
Log.i(TAG, "Removing location updates");
|
||||
try {
|
||||
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
|
||||
Utils.setRequestingLocationUpdates(this, false);
|
||||
stopSelf();
|
||||
} catch (SecurityException unlikely) {
|
||||
//When we lost permission
|
||||
Utils.setRequestingLocationUpdates(this, true);
|
||||
Log.i(TAG, "No location permission");
|
||||
}
|
||||
Utils.setRequestingLocationUpdates(this, false);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@ -252,7 +183,7 @@ public class LocationUpdatesService extends Service {
|
||||
.setPriority(-1)
|
||||
.setContentTitle(Utils.getLocationTitle(mLocation))
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(R.mipmap.ic_launcher)
|
||||
.setSmallIcon(R.drawable.mini_icon)
|
||||
.setWhen(System.currentTimeMillis());
|
||||
|
||||
return builder.build();
|
||||
@ -278,21 +209,12 @@ public class LocationUpdatesService extends Service {
|
||||
|
||||
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());
|
||||
}
|
||||
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
|
||||
|
||||
Constraints constraints = new Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build();
|
||||
|
||||
// Create the Data object:
|
||||
Data locationData = new Data.Builder()
|
||||
.putDouble("Lat", mLocation.getLatitude())
|
||||
.putDouble("Long", mLocation.getLongitude())
|
||||
@ -315,42 +237,9 @@ public class LocationUpdatesService extends Service {
|
||||
.enqueueUniqueWork("SendLocationUpdate", ExistingWorkPolicy.REPLACE, uploadWorkRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location request parameters.
|
||||
*/
|
||||
private void createLocationRequest() {
|
||||
mLocationRequest = new LocationRequest();
|
||||
mLocationRequest.setInterval(requestInterval);
|
||||
mLocationRequest.setFastestInterval(requestInterval);
|
||||
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;
|
||||
}
|
||||
}
|
@ -20,7 +20,6 @@ import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.widget.Toast;
|
||||
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
@ -34,8 +33,6 @@ public class MainActivity extends FlutterActivity {
|
||||
|
||||
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
|
||||
|
||||
private MyReceiver myReceiver;
|
||||
|
||||
private LocationUpdatesService mService = null;
|
||||
|
||||
private boolean mBound = false;
|
||||
@ -87,8 +84,8 @@ public class MainActivity extends FlutterActivity {
|
||||
}
|
||||
break;
|
||||
case "startLocationService":
|
||||
if (checkPermissions()) {
|
||||
requestPermissions();
|
||||
if (isNoLocationPermissions()) {
|
||||
requestLocationPermissions();
|
||||
} else {
|
||||
mService.requestLocationUpdates();
|
||||
}
|
||||
@ -110,12 +107,11 @@ public class MainActivity extends FlutterActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
myReceiver = new MyReceiver();
|
||||
if (Utils.requestingLocationUpdates(this)) {
|
||||
if (checkPermissions()) {
|
||||
requestPermissions();
|
||||
/*if (Utils.requestingLocationUpdates(this)) {
|
||||
if (isNoLocationPermissions()) {
|
||||
requestLocationPermissions();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -128,13 +124,10 @@ public class MainActivity extends FlutterActivity {
|
||||
@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();
|
||||
}
|
||||
|
||||
@ -147,12 +140,12 @@ public class MainActivity extends FlutterActivity {
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
private boolean checkPermissions() {
|
||||
private boolean isNoLocationPermissions() {
|
||||
return PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
}
|
||||
|
||||
private void requestPermissions() {
|
||||
private void requestLocationPermissions() {
|
||||
ActivityCompat.requestPermissions(MainActivity.this,
|
||||
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
|
||||
REQUEST_PERMISSIONS_REQUEST_CODE);
|
||||
@ -168,14 +161,4 @@ public class MainActivity extends FlutterActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private class MyReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
|
||||
if (location != null) {
|
||||
//TODO looks like we need to remove this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ public class MessagingService extends FirebaseMessagingService {
|
||||
}
|
||||
broadcastIntent.putExtra("actionData", data.get("action" + i + "_data"));
|
||||
PendingIntent actionIntent = PendingIntent.getBroadcast(this, i, broadcastIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
notificationBuilder.addAction(R.drawable.mini_icon, data.get("action" + i), actionIntent);
|
||||
notificationBuilder.addAction(R.drawable.blank_icon, data.get("action" + i), actionIntent);
|
||||
}
|
||||
}
|
||||
NotificationManager notificationManager =
|
||||
|
Reference in New Issue
Block a user