Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/gps/GBLocationService.java

185 lines
7.3 KiB
Java

/* Copyright (C) 2022-2024 halemmerich, José Rebelo, LukasEdl, Martin Boonk
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.externalevents.gps;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.PendingIntentUtils;
/**
* A static location manager, which keeps track of what providers are currently running. A notification is kept
* while there is at least one provider running.
*/
public class GBLocationService extends BroadcastReceiver {
private static final Logger LOG = LoggerFactory.getLogger(GBLocationService.class);
public static final String ACTION_START = "GBLocationService.START";
public static final String ACTION_STOP = "GBLocationService.STOP";
public static final String ACTION_STOP_ALL = "GBLocationService.STOP_ALL";
public static final String EXTRA_TYPE = "extra_type";
public static final String EXTRA_INTERVAL = "extra_interval";
private final Context context;
private final Map<GBDevice, List<GBLocationProvider>> providersByDevice = new HashMap<>();
public GBLocationService(final Context context) {
this.context = context;
}
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction() == null) {
LOG.warn("Action is null");
return;
}
final GBDevice device = intent.getParcelableExtra(GBDevice.EXTRA_DEVICE);
switch (intent.getAction()) {
case ACTION_START:
if (device == null) {
LOG.error("Device is null for {}", intent.getAction());
return;
}
final GBLocationProviderType providerType = GBLocationProviderType.valueOf(
intent.hasExtra(EXTRA_TYPE) ? intent.getStringExtra(EXTRA_TYPE) : "GPS"
);
final int updateInterval = intent.getIntExtra(EXTRA_INTERVAL, 1000);
LOG.debug("Starting location provider {} for {}", providerType, device.getAliasOrName());
if (!providersByDevice.containsKey(device)) {
providersByDevice.put(device, new ArrayList<>());
}
updateNotification();
final List<GBLocationProvider> existingProviders = providersByDevice.get(device);
final GBLocationListener locationListener = new GBLocationListener(device);
final GBLocationProvider locationProvider = providerType.newInstance(context, locationListener);
locationProvider.start(updateInterval);
Objects.requireNonNull(existingProviders).add(locationProvider);
return;
case ACTION_STOP:
if (device != null) {
stopDevice(device);
updateNotification();
} else {
stopAll();
}
return;
case ACTION_STOP_ALL:
stopAll();
return;
default:
LOG.warn("Unknown action {}", intent.getAction());
}
}
public void stopDevice(final GBDevice device) {
LOG.debug("Stopping location providers for {}", device.getAliasOrName());
final List<GBLocationProvider> providers = providersByDevice.remove(device);
if (providers != null) {
for (final GBLocationProvider provider : providers) {
provider.stop();
}
}
}
public IntentFilter buildFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_START);
intentFilter.addAction(ACTION_STOP);
return intentFilter;
}
public void stopAll() {
LOG.info("Stopping location service for all devices");
final List<GBDevice> gbDevices = new ArrayList<>(providersByDevice.keySet());
for (GBDevice d : gbDevices) {
stopDevice(d);
}
updateNotification();
}
public static void start(final Context context,
@NonNull final GBDevice device,
final GBLocationProviderType providerType,
final int updateInterval) {
final Intent intent = new Intent(ACTION_START);
intent.putExtra(GBDevice.EXTRA_DEVICE, device);
intent.putExtra(EXTRA_TYPE, providerType.name());
intent.putExtra(EXTRA_INTERVAL, updateInterval);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
public static void stop(final Context context, @Nullable final GBDevice device) {
final Intent intent = new Intent(ACTION_STOP);
intent.putExtra(GBDevice.EXTRA_DEVICE, device);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
}
private void updateNotification() {
if (!providersByDevice.isEmpty()) {
final Intent notificationIntent = new Intent(context, GBLocationService.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
final PendingIntent pendingIntent = PendingIntentUtils.getActivity(context, 0, notificationIntent, 0, false);
final NotificationCompat.Builder nb = new NotificationCompat.Builder(context, GB.NOTIFICATION_CHANNEL_ID_GPS)
.setTicker(context.getString(R.string.notification_gps_title))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentTitle(context.getString(R.string.notification_gps_title))
.setContentText(context.getString(R.string.notification_gps_text, providersByDevice.size()))
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_gps_location)
.setOngoing(true);
GB.notify(GB.NOTIFICATION_ID_GPS, nb.build(), context);
} else {
GB.removeNotification(GB.NOTIFICATION_ID_GPS, context);
}
}
}