Use new RPC for updating and stopping location streaming (#4400)

This commit is contained in:
wchen342
2026-05-26 12:02:13 -04:00
committed by GitHub
parent 2bb3ad546e
commit d019c1bfb5
4 changed files with 93 additions and 95 deletions
@@ -1,53 +0,0 @@
package org.thoughtcrime.securesms.geolocation;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.HashSet;
import java.util.Set;
public final class ActiveLocationChats {
private static final String PREFS_NAME = "location_streaming";
private static final String KEY_ACTIVE = "active_chat_ids";
private ActiveLocationChats() {}
private static SharedPreferences prefs(Context context) {
return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
}
/**
* Add a chat. Uses commit() to guarantee the write reaches disk before the process can die to
* preserve the superset invariant.
*/
static void add(Context context, int chatId) {
Set<String> current = new HashSet<>(getAll(context));
current.add(String.valueOf(chatId));
prefs(context).edit().putStringSet(KEY_ACTIVE, current).commit();
}
public static void remove(Context context, int chatId) {
Set<String> current = new HashSet<>(getAll(context));
current.remove(String.valueOf(chatId));
prefs(context).edit().putStringSet(KEY_ACTIVE, current).apply();
}
static void clear(Context context) {
prefs(context).edit().remove(KEY_ACTIVE).apply();
}
static Set<Integer> getAllIds(Context context) {
Set<Integer> ids = new HashSet<>();
for (String s : getAll(context)) {
try {
ids.add(Integer.parseInt(s));
} catch (NumberFormatException ignored) {
}
}
return ids;
}
private static Set<String> getAll(Context context) {
return prefs(context).getStringSet(KEY_ACTIVE, new HashSet<>());
}
}
@@ -17,13 +17,15 @@ import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.connect.DcHelper;
public class LocationStreamingService extends Service {
private static final String TAG = "LocationStreamingService";
private static final String TAG = "LocationStreamingSvc";
private static final String ACTION_STOP = "org.thoughtcrime.securesms.geolocation.STOP_STREAMING";
private static final int NOTIFICATION_ID = 8801;
private static final String CHANNEL_ID = "location_streaming";
@@ -36,38 +38,63 @@ public class LocationStreamingService extends Service {
// static API
/** Register a chat for location updates, then ensure the service is running. */
public static void startSharing(Context context, int chatId, int durationSeconds) {
ActiveLocationChats.add(context, chatId);
DcHelper.getContext(context).sendLocationsToChat(chatId, durationSeconds);
ContextCompat.startForegroundService(
context, new Intent(context, LocationStreamingService.class));
}
/** Unregister a chat. If no chats remain, stop the service. */
public static void stopSharing(Context context, int chatId) {
ActiveLocationChats.remove(context, chatId);
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
if (!DcHelper.getContext(context).isSendingLocationsToChat(0)) {
context.stopService(new Intent(context, LocationStreamingService.class));
}
}
public static void ensureRunning(Context context) {
if (!hasLocationPermission(context)) {
for (int chatId : ActiveLocationChats.getAllIds(context)) {
DcHelper.getContext(context).sendLocationsToChat(chatId, 0);
}
ActiveLocationChats.clear(context);
public static void startSharing(Context context, int accountId, int chatId, int durationSeconds) {
try {
DcHelper.getRpc(context).sendLocationsToChat(accountId, chatId, durationSeconds);
} catch (RpcException e) {
Log.e(TAG, "Failed to start location streaming", e);
return;
}
ContextCompat.startForegroundService(
context, new Intent(context, LocationStreamingService.class));
}
/** Unregister a chat. If no chats remain, stop the service. */
public static void stopSharing(Context context, int accountId, int chatId) {
try {
DcHelper.getRpc(context).sendLocationsToChat(accountId, chatId, 0);
} catch (RpcException e) {
Log.e(TAG, "Failed to stop location streaming", e);
}
if (!isAnySharingActive(context)) {
context.stopService(new Intent(context, LocationStreamingService.class));
}
}
public static void ensureRunning(Context context) {
if (!hasLocationPermission(context)) {
try {
DcHelper.getRpc(context).stopSendingLocations();
} catch (RpcException e) {
Log.e(TAG, "Failed to stop location streaming", e);
}
return;
}
if (isAnySharingActive(context)) {
ContextCompat.startForegroundService(
context, new Intent(context, LocationStreamingService.class));
}
}
public static boolean isRunning() {
return running;
}
private static boolean isAnySharingActive(Context context) {
try {
Rpc rpc = DcHelper.getRpc(context);
for (int accountId : rpc.getAllAccountIds()) {
if (rpc.isSendingLocations(accountId)) {
return true;
}
}
} catch (RpcException e) {
Log.e(TAG, "Failed to check location streaming state", e);
return true;
}
return false;
}
// lifecycle
@Override
@@ -86,7 +113,7 @@ public class LocationStreamingService extends Service {
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
if (intent != null && ACTION_STOP.equals(intent.getAction())) {
stopAllSharing();
stopAllSharing(this);
stopSelf();
return START_NOT_STICKY;
}
@@ -100,11 +127,12 @@ public class LocationStreamingService extends Service {
return START_STICKY;
}
private void stopAllSharing() {
for (int chatId : ActiveLocationChats.getAllIds(this)) {
DcHelper.getContext(this).sendLocationsToChat(chatId, 0);
private static void stopAllSharing(Context context) {
try {
DcHelper.getRpc(context).stopSendingLocations();
} catch (RpcException e) {
Log.e(TAG, "Failed to stop sending locations", e);
}
ActiveLocationChats.clear(this);
}
@Nullable
@@ -150,16 +178,21 @@ public class LocationStreamingService extends Service {
private void publishAndWrite(Location location) {
LocationData.getInstance().post(location);
boolean keepGoing =
DcHelper.getContext(this)
.setLocation(
(float) location.getLatitude(),
(float) location.getLongitude(),
location.getAccuracy());
boolean keepGoing;
try {
keepGoing =
DcHelper.getRpc(this)
.setLocation(
(float) location.getLatitude(),
(float) location.getLongitude(),
location.getAccuracy());
} catch (RpcException e) {
Log.e(TAG, "Failed to set location", e);
return;
}
Log.d(TAG, "keepGoing: " + keepGoing);
if (!keepGoing) {
stopAllSharing();
stopSelf();
}
}
@@ -36,6 +36,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import chat.delta.util.ListenableFuture;
import chat.delta.util.SettableFuture;
@@ -63,7 +64,6 @@ import org.thoughtcrime.securesms.components.audioplay.AudioPlaybackViewModel;
import org.thoughtcrime.securesms.components.audioplay.AudioView;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.database.AttachmentDatabase;
import org.thoughtcrime.securesms.geolocation.ActiveLocationChats;
import org.thoughtcrime.securesms.geolocation.LocationStreamingService;
import org.thoughtcrime.securesms.permissions.Permissions;
import org.thoughtcrime.securesms.providers.PersistentBlobProvider;
@@ -491,16 +491,29 @@ public class AttachmentManager {
public static void selectLocation(Activity activity, int chatId) {
Context appContext = activity.getApplicationContext();
Rpc rpc = DcHelper.getRpc(appContext);
int accountId = DcHelper.getContext(appContext).getAccountId();
if (DcHelper.getContext(appContext).isSendingLocationsToChat(chatId)) {
boolean currentlySharing;
try {
currentlySharing = rpc.isSendingLocationsToChat(accountId, chatId);
} catch (RpcException e) {
Log.e(TAG, "Failed to check location streaming state", e);
return;
}
if (currentlySharing) {
if (LocationStreamingService.isRunning()) {
LocationStreamingService.stopSharing(appContext, chatId);
LocationStreamingService.stopSharing(appContext, accountId, chatId);
return;
}
// Stale — service is dead but chat layer still thinks it's sharing.
// Clean up this chat and fall through to the fresh start flow.
ActiveLocationChats.remove(appContext, chatId);
DcHelper.getContext(appContext).sendLocationsToChat(chatId, 0);
// Clean up and fall through to the fresh start flow.
try {
rpc.sendLocationsToChat(accountId, chatId, 0);
} catch (RpcException e) {
Log.e(TAG, "Failed to stop stale location streaming", e);
}
}
Permissions.with(activity)
@@ -514,7 +527,8 @@ public class AttachmentManager {
ShareLocationDialog.show(
activity,
durationInSeconds ->
LocationStreamingService.startSharing(appContext, chatId, durationInSeconds));
LocationStreamingService.startSharing(
appContext, accountId, chatId, durationInSeconds));
})
.request(
android.Manifest.permission.ACCESS_FINE_LOCATION,