Compare commits

..

15 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 469259f469 Fix LocationListenerCompat compilation error - use correct interface for LocationManagerCompat
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 21:53:16 +00:00
copilot-swe-agent[bot] 9dcd75fe54 Use LocationManagerCompat for proper location updates on modern Android
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 21:47:02 +00:00
copilot-swe-agent[bot] adffc4f6f8 Fix foreground service for Android 14+ and add NETWORK_PROVIDER fallback
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 21:22:29 +00:00
copilot-swe-agent[bot] 7633338359 Fix PermissionsBuilder method chaining - request and execute must be separate calls
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:43:58 +00:00
adb 2f4d239e2b Merge branch 'main' into copilot/allow-location-sharing-again 2025-11-24 21:38:50 +01:00
copilot-swe-agent[bot] ca65424653 Ensure foreground service requirements are met even on error
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:22:58 +00:00
copilot-swe-agent[bot] 6e48933a8f Remove orphaned code from LocationBackgroundService
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:21:00 +00:00
copilot-swe-agent[bot] df50a6d608 Fix method call, thread safety, and service initialization order
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:19:30 +00:00
copilot-swe-agent[bot] b3abc39059 Add FLAG_IMMUTABLE and improve foreground service initialization
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:17:30 +00:00
copilot-swe-agent[bot] 61fc7019b2 Improve foreground service lifecycle handling
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:15:09 +00:00
copilot-swe-agent[bot] b8d6c0b481 Fix service binding logic and foreground service lifecycle
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:13:51 +00:00
copilot-swe-agent[bot] 8675f816de Fix code review feedback: improve error handling and service logic
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:12:34 +00:00
copilot-swe-agent[bot] c9af561868 Add FOREGROUND_SERVICE_LOCATION permission
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:10:28 +00:00
copilot-swe-agent[bot] db10790ecd Implement foreground notification for location sharing
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2025-11-24 20:07:35 +00:00
copilot-swe-agent[bot] 09f389d6de Initial plan 2025-11-24 20:00:57 +00:00
13 changed files with 165 additions and 436 deletions
+1 -4
View File
@@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<!-- force compiling emojipicker on sdk<21; runtime checks are required then -->
<uses-sdk tools:overrideLibrary="androidx.emoji2.emojipicker"/>
@@ -240,10 +241,6 @@
android:exported="true">
</activity>
<activity android:name=".TransportListActivity"
android:windowSoftInputMode="stateHidden"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name=".proxy.ProxySettingsActivity"
android:label="@string/proxy_settings"
android:windowSoftInputMode="stateHidden"
@@ -119,20 +119,9 @@ public class EditTransportActivity extends BaseActionBarActivity implements DcEv
});
EnteredLoginParam config = null;
String transportAddr = getIntent().getStringExtra(TransportListActivity.EXTRA_TRANSPORT_ADDR);
try {
List<EnteredLoginParam> relays = rpc.listTransports(accId);
if (transportAddr != null) {
// Find the transport with the specified address
for (EnteredLoginParam transport : relays) {
if (transportAddr.equals(transport.addr)) {
config = transport;
break;
}
}
} else if (!relays.isEmpty()) {
config = relays.get(0);
}
if (!relays.isEmpty()) config = relays.get(0);
} catch (RpcException ignored) {}
ActionBar actionBar = getSupportActionBar();
@@ -1,156 +0,0 @@
package org.thoughtcrime.securesms;
import android.content.Intent;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.util.Util;
import java.util.List;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import chat.delta.rpc.types.EnteredLoginParam;
public class TransportListActivity extends BaseActionBarActivity
implements TransportListAdapter.OnTransportClickListener {
public static final String EXTRA_TRANSPORT_ADDR = "transport_addr";
private RecyclerView recyclerView;
private TransportListAdapter adapter;
private FloatingActionButton fabAdd;
private Rpc rpc;
private int accId;
private String mainTransportAddr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transport_list);
rpc = DcHelper.getRpc(this);
accId = DcHelper.getContext(this).getAccountId();
// Setup toolbar
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(R.string.transports);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
recyclerView = findViewById(R.id.transport_list);
fabAdd = findViewById(R.id.fab_add_transport);
adapter = new TransportListAdapter(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
fabAdd.setOnClickListener(v -> openAddTransport());
// Handle window insets for FAB to stay above navigation bar
ViewCompat.setOnApplyWindowInsetsListener(fabAdd, (v, windowInsets) -> {
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
CoordinatorLayout.LayoutParams fabParams = (CoordinatorLayout.LayoutParams) fabAdd.getLayoutParams();
int fabMargin = (int) (16 * getResources().getDisplayMetrics().density);
fabParams.bottomMargin = insets.bottom + fabMargin;
fabParams.rightMargin = fabMargin;
fabAdd.setLayoutParams(fabParams);
return windowInsets;
});
loadTransports();
}
@Override
protected void onResume() {
super.onResume();
loadTransports();
}
private void loadTransports() {
Util.runOnBackground(() -> {
try {
List<EnteredLoginParam> transports = rpc.listTransports(accId);
mainTransportAddr = DcHelper.get(this, DcHelper.CONFIG_CONFIGURED_ADDRESS);
Util.runOnMain(() -> adapter.setTransports(transports, mainTransportAddr));
} catch (RpcException e) {
mainTransportAddr = DcHelper.get(this, DcHelper.CONFIG_CONFIGURED_ADDRESS);
Util.runOnMain(() -> adapter.setTransports(null, mainTransportAddr));
}
});
}
private void openAddTransport() {
Intent intent = new Intent(this, EditTransportActivity.class);
startActivity(intent);
}
@Override
public void onTransportClick(EnteredLoginParam transport) {
// Clicking on a transport makes it the main transport
if (transport.addr != null && !transport.addr.equals(mainTransportAddr)) {
Util.runOnBackground(() -> {
DcHelper.getContext(this).setConfig(DcHelper.CONFIG_CONFIGURED_ADDRESS, transport.addr);
Util.runOnMain(this::loadTransports);
});
}
}
@Override
public void onTransportEdit(EnteredLoginParam transport) {
Intent intent = new Intent(this, EditTransportActivity.class);
intent.putExtra(EXTRA_TRANSPORT_ADDR, transport.addr);
startActivity(intent);
}
@Override
public void onTransportDelete(EnteredLoginParam transport) {
new AlertDialog.Builder(this)
.setTitle(R.string.remove_transport)
.setMessage(getString(R.string.confirm_remove_transport, transport.addr))
.setPositiveButton(R.string.ok, (dialog, which) -> deleteTransport(transport))
.setNegativeButton(R.string.cancel, null)
.show();
}
private void deleteTransport(EnteredLoginParam transport) {
Util.runOnBackground(() -> {
try {
rpc.deleteTransport(accId, transport.addr);
Util.runOnMain(this::loadTransports);
} catch (RpcException e) {
Util.runOnMain(() -> {
new AlertDialog.Builder(this)
.setMessage(getString(R.string.error) + ": " + e.getMessage())
.setPositiveButton(R.string.ok, null)
.show();
});
}
});
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
@@ -1,106 +0,0 @@
package org.thoughtcrime.securesms;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import chat.delta.rpc.types.EnteredLoginParam;
public class TransportListAdapter extends RecyclerView.Adapter<TransportListAdapter.TransportViewHolder> {
private List<EnteredLoginParam> transports = new ArrayList<>();
private final OnTransportClickListener listener;
private String mainTransportAddr;
public interface OnTransportClickListener {
void onTransportClick(EnteredLoginParam transport);
void onTransportEdit(EnteredLoginParam transport);
void onTransportDelete(EnteredLoginParam transport);
}
public TransportListAdapter(OnTransportClickListener listener) {
this.listener = listener;
}
public void setTransports(List<EnteredLoginParam> transports, String mainTransportAddr) {
this.transports = transports != null ? transports : new ArrayList<>();
this.mainTransportAddr = mainTransportAddr;
notifyDataSetChanged();
}
@NonNull
@Override
public TransportViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.transport_list_item, parent, false);
return new TransportViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull TransportViewHolder holder, int position) {
EnteredLoginParam transport = transports.get(position);
boolean isMain = transport.addr != null && transport.addr.equals(mainTransportAddr);
holder.bind(transport, isMain, listener);
}
@Override
public int getItemCount() {
return transports.size();
}
static class TransportViewHolder extends RecyclerView.ViewHolder {
private final TextView emailText;
private final TextView serverText;
private final ImageView mainIndicator;
private final ImageView editButton;
private final ImageView deleteButton;
public TransportViewHolder(@NonNull View itemView) {
super(itemView);
emailText = itemView.findViewById(R.id.transport_email);
serverText = itemView.findViewById(R.id.transport_server);
mainIndicator = itemView.findViewById(R.id.main_transport_indicator);
editButton = itemView.findViewById(R.id.edit_button);
deleteButton = itemView.findViewById(R.id.delete_button);
}
public void bind(EnteredLoginParam transport, boolean isMain, OnTransportClickListener listener) {
emailText.setText(transport.addr);
String serverInfo = "";
if (transport.imapServer != null && !transport.imapServer.isEmpty()) {
serverInfo = transport.imapServer;
}
serverText.setText(serverInfo);
serverText.setVisibility(serverInfo.isEmpty() ? View.GONE : View.VISIBLE);
mainIndicator.setVisibility(isMain ? View.VISIBLE : View.GONE);
itemView.setOnClickListener(v -> {
if (listener != null) {
listener.onTransportClick(transport);
}
});
editButton.setOnClickListener(v -> {
if (listener != null) {
listener.onTransportEdit(transport);
}
});
deleteButton.setOnClickListener(v -> {
if (listener != null) {
listener.onTransportDelete(transport);
}
});
}
}
}
@@ -5,9 +5,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.connect.DcHelper;
import java.util.LinkedList;
@@ -23,11 +26,13 @@ public class DcLocationManager implements Observer {
private final Context context;
private DcLocation dcLocation = DcLocation.getInstance();
private final LinkedList<Integer> pendingShareLastLocation = new LinkedList<>();
private boolean serviceBound = false;
private final ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "background service connected");
serviceBinder = (LocationBackgroundService.LocationBackgroundServiceBinder) service;
serviceBound = true;
while (!pendingShareLastLocation.isEmpty()) {
shareLastLocation(pendingShareLastLocation.pop());
}
@@ -37,6 +42,7 @@ public class DcLocationManager implements Observer {
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "background service disconnected");
serviceBinder = null;
serviceBound = false;
}
};
@@ -49,19 +55,29 @@ public class DcLocationManager implements Observer {
}
public void startLocationEngine() {
if (serviceBinder == null) {
if (serviceBinder == null || !serviceBound) {
Intent intent = new Intent(context.getApplicationContext(), LocationBackgroundService.class);
// Start as foreground service
ContextCompat.startForegroundService(context, intent);
// Then bind to it
context.bindService(intent, serviceConnection, BIND_AUTO_CREATE);
}
}
public void stopLocationEngine() {
if (serviceBinder == null) {
if (serviceBinder == null || !serviceBound) {
return;
}
context.unbindService(serviceConnection);
serviceBinder.stop();
try {
context.unbindService(serviceConnection);
if (serviceBinder != null) {
serviceBinder.stop();
}
} catch (IllegalArgumentException e) {
Log.w(TAG, "Service not registered", e);
}
serviceBinder = null;
serviceBound = false;
}
public void stopSharingLocation(int chatId) {
@@ -1,18 +1,36 @@
package org.thoughtcrime.securesms.geolocation;
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.ServiceConnection;
import android.content.pm.ServiceInfo;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import androidx.core.location.LocationListenerCompat;
import androidx.core.location.LocationManagerCompat;
import androidx.core.location.LocationRequestCompat;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.notifications.NotificationCenter;
import org.thoughtcrime.securesms.util.IntentUtils;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
public class LocationBackgroundService extends Service {
@@ -22,6 +40,7 @@ public class LocationBackgroundService extends Service {
private static final int LOCATION_INTERVAL = 1000;
private static final float LOCATION_DISTANCE = 25F;
ServiceLocationListener locationListener;
private final AtomicBoolean isForeground = new AtomicBoolean(false);
private final IBinder mBinder = new LocationBackgroundServiceBinder();
@@ -37,12 +56,21 @@ public class LocationBackgroundService extends Service {
@Override
public void onCreate() {
super.onCreate();
locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
if (locationManager == null) {
Log.e(TAG, "Unable to initialize location service");
// Must start foreground to avoid crash, then stop immediately
initializeForegroundService();
stopForeground(true);
stopSelf();
return;
}
// Initialize foreground service after successful location manager setup
initializeForegroundService();
locationListener = new ServiceLocationListener();
Location lastLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastLocation != null) {
@@ -51,14 +79,19 @@ public class LocationBackgroundService extends Service {
DcLocation.getInstance().updateLocation(lastLocation);
}
}
//requestLocationUpdate(LocationManager.NETWORK_PROVIDER);
// Request location updates from both GPS and network providers for better coverage
requestLocationUpdate(LocationManager.GPS_PROVIDER);
requestLocationUpdate(LocationManager.NETWORK_PROVIDER);
initialLocationUpdate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// Ensure foreground notification is shown (handles edge cases)
initializeForegroundService();
return START_STICKY;
}
@@ -66,23 +99,93 @@ public class LocationBackgroundService extends Service {
public void onDestroy() {
super.onDestroy();
if (locationManager == null) {
// Stop foreground notification
stopForeground(true);
if (locationManager == null || locationListener == null) {
return;
}
try {
locationManager.removeUpdates(locationListener);
LocationManagerCompat.removeUpdates(locationManager, locationListener);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listeners, ignore", ex);
}
}
private void initializeForegroundService() {
if (isForeground.compareAndSet(false, true)) {
createNotificationChannel();
Notification notification = createNotification();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Android 14+ requires foregroundServiceType in startForeground
ServiceCompat.startForeground(this, NotificationCenter.ID_LOCATION, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
} else {
startForeground(NotificationCenter.ID_LOCATION, notification);
}
Log.d(TAG, "Foreground service started with notification");
}
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
NotificationCenter.CH_LOCATION,
getString(R.string.location),
NotificationManager.IMPORTANCE_LOW
);
channel.setDescription("Location sharing notification");
NotificationManager notificationManager = getSystemService(NotificationManager.class);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
}
}
private Notification createNotification() {
Intent intent = new Intent(this, ConversationListActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
0,
intent,
IntentUtils.FLAG_IMMUTABLE()
);
return new NotificationCompat.Builder(this, NotificationCenter.CH_LOCATION)
.setContentTitle(getString(R.string.location_sharing_notification_title))
.setContentText(getString(R.string.location_sharing_notification_text))
.setSmallIcon(R.drawable.ic_location_on_white_24dp)
.setContentIntent(pendingIntent)
.setOngoing(true)
.setPriority(NotificationCompat.PRIORITY_LOW)
.build();
}
private void requestLocationUpdate(String provider) {
try {
locationManager.requestLocationUpdates(
provider, LOCATION_INTERVAL, LOCATION_DISTANCE,
locationListener);
} catch (SecurityException | IllegalArgumentException ex) {
// Check if provider is available
if (!locationManager.isProviderEnabled(provider)) {
Log.w(TAG, String.format("Provider %s is not enabled", provider));
return;
}
// Use LocationManagerCompat for better compatibility with modern Android
LocationRequestCompat locationRequest = new LocationRequestCompat.Builder(LOCATION_INTERVAL)
.setMinUpdateDistanceMeters(LOCATION_DISTANCE)
.setQuality(LocationRequestCompat.QUALITY_HIGH_ACCURACY)
.build();
Executor executor = ContextCompat.getMainExecutor(this);
LocationManagerCompat.requestLocationUpdates(
locationManager,
provider,
locationRequest,
executor,
locationListener
);
Log.d(TAG, String.format("Requested location updates from %s provider", provider));
} catch (SecurityException | IllegalArgumentException ex) {
Log.e(TAG, String.format("Unable to request %s provider based location updates.", provider), ex);
}
}
@@ -93,9 +196,16 @@ public class LocationBackgroundService extends Service {
if (gpsLocation != null && System.currentTimeMillis() - gpsLocation.getTime() < INITIAL_TIMEOUT) {
locationListener.onLocationChanged(gpsLocation);
}
// Also try network provider for initial location
Location networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if (networkLocation != null && System.currentTimeMillis() - networkLocation.getTime() < INITIAL_TIMEOUT) {
// Use network location if GPS location is not available or network location is newer
if (gpsLocation == null || networkLocation.getTime() > gpsLocation.getTime()) {
locationListener.onLocationChanged(networkLocation);
}
}
} catch (NullPointerException | SecurityException e) {
e.printStackTrace();
Log.e(TAG, "Error getting initial location", e);
}
}
@@ -110,30 +220,22 @@ public class LocationBackgroundService extends Service {
}
}
private class ServiceLocationListener implements LocationListener {
private class ServiceLocationListener implements LocationListenerCompat {
@Override
public void onLocationChanged(@NonNull Location location) {
Log.d(TAG, "onLocationChanged: " + location);
if (location == null) {
return;
}
DcLocation.getInstance().updateLocation(location);
}
@Override
public void onProviderDisabled(@NonNull String provider) {
Log.e(TAG, "onProviderDisabled: " + provider);
Log.w(TAG, "onProviderDisabled: " + provider);
}
@Override
public void onProviderEnabled(@NonNull String provider) {
Log.e(TAG, "onProviderEnabled: " + provider);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
Log.e(TAG, "onStatusChanged: " + provider + " status: " + status);
Log.d(TAG, "onProviderEnabled: " + provider);
}
}
@@ -484,7 +484,7 @@ public class AttachmentManager {
// for rationale dialog requirements
Permissions.PermissionsBuilder permissionsBuilder = Permissions.with(activity)
.ifNecessary()
.withRationaleDialog("To share your live location with chat members, allow ArcaneChat to use your location data.\n\nTo make live location work gaplessly, location data is used even when the app is closed or not in use.", R.drawable.ic_location_on_white_24dp)
.withRationaleDialog("To share your live location with chat members, allow ArcaneChat to use your location data.", R.drawable.ic_location_on_white_24dp)
.withPermanentDenialDialog(activity.getString(R.string.perm_explain_access_to_location_denied))
.onAllGranted(() -> {
ShareLocationDialog.show(activity, durationInSeconds -> {
@@ -495,11 +495,7 @@ public class AttachmentManager {
}
});
});
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
permissionsBuilder.request(Manifest.permission.ACCESS_BACKGROUND_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION);
} else {
permissionsBuilder.request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION);
}
permissionsBuilder.request(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION);
permissionsBuilder.execute();
}
@@ -218,6 +218,7 @@ public class NotificationCenter {
public static final int ID_MSG_SUMMARY = 2;
public static final int ID_GENERIC = 3;
public static final int ID_FETCH = 4;
public static final int ID_LOCATION = 5;
public static final int ID_MSG_OFFSET = 0; // msgId is added - as msgId start at 10, there are no conflicts with lower numbers
@@ -243,6 +244,7 @@ public class NotificationCenter {
public static final String CH_MSG_VERSION = "5";
public static final String CH_PERMANENT = "dc_fg_notification_ch";
public static final String CH_GENERIC = "ch_generic";
public static final String CH_LOCATION = "ch_location";
public static final String CH_CALLS_PREFIX = "call_chan";
private boolean notificationChannelsSupported() {
@@ -31,7 +31,7 @@ import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.ConversationActivity;
import org.thoughtcrime.securesms.LogViewActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.TransportListActivity;
import org.thoughtcrime.securesms.EditTransportActivity;
import org.thoughtcrime.securesms.connect.DcEventCenter;
import org.thoughtcrime.securesms.connect.DcHelper;
import org.thoughtcrime.securesms.proxy.ProxySettingsActivity;
@@ -166,7 +166,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
passwordAndAccount.setOnPreferenceClickListener(((preference) -> {
boolean result = ScreenLockUtil.applyScreenLock(requireActivity(), getString(R.string.edit_transport), getString(R.string.enter_system_secret_to_continue), REQUEST_CODE_CONFIRM_CREDENTIALS_ACCOUNT);
if (!result) {
openTransportListActivity();
openRegistrationActivity();
}
return true;
}));
@@ -203,7 +203,7 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == REQUEST_CODE_CONFIRM_CREDENTIALS_ACCOUNT) {
openTransportListActivity();
openRegistrationActivity();
}
}
@@ -270,8 +270,8 @@ public class AdvancedPreferenceFragment extends ListSummaryPreferenceFragment
}
}
private void openTransportListActivity() {
Intent intent = new Intent(requireActivity(), TransportListActivity.class);
private void openRegistrationActivity() {
Intent intent = new Intent(requireActivity(), EditTransportActivity.class);
startActivity(intent);
}
@@ -39,4 +39,12 @@ public class IntentUtils {
return 0;
}
}
public static int FLAG_IMMUTABLE() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return PendingIntent.FLAG_IMMUTABLE;
} else {
return 0;
}
}
}
@@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:background="?attr/colorPrimary">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:contentInsetStart="14dp"
app:contentInsetLeft="14dp"
android:theme="?attr/actionBarStyle" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/transport_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/windowBackground"
android:clipToPadding="false"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/transport_list_item" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_add_transport"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="16dp"
android:contentDescription="@string/add_transport"
android:src="@drawable/ic_add_white_24dp" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
android:background="?attr/selectableItemBackground"
android:minHeight="72dp"
android:gravity="center_vertical">
<ImageView
android:id="@+id/main_transport_indicator"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:src="@drawable/ic_check_white_24dp"
android:tint="?attr/colorPrimary"
android:contentDescription="@string/main"
android:visibility="gone"
tools:visibility="visible" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/transport_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
tools:text="user@example.com" />
<TextView
android:id="@+id/transport_server"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
tools:text="imap.example.com" />
</LinearLayout>
<ImageView
android:id="@+id/edit_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:src="@drawable/ic_create_white_24dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/edit_transport"
android:tint="?android:attr/textColorSecondary" />
<ImageView
android:id="@+id/delete_button"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="12dp"
android:src="@drawable/ic_delete_white_24dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/remove_transport"
android:tint="?android:attr/textColorSecondary" />
</LinearLayout>
+2 -1
View File
@@ -358,6 +358,8 @@
<string name="copy_json">Copy JSON</string>
<string name="replace_draft">Replace Draft</string>
<string name="title_share_location">Share location with all group members</string>
<string name="location_sharing_notification_title">Sharing location</string>
<string name="location_sharing_notification_text">Location is being shared with chat members</string>
<string name="device_talk">Device Messages</string>
<string name="device_talk_subtitle">Locally generated messages</string>
<string name="device_talk_explain">Messages in this chat are generated on your device to inform about app updates and problems during usage.</string>
@@ -693,7 +695,6 @@
<string name="add_transport">Add Transport</string>
<string name="remove_transport">Remove Transport</string>
<string name="edit_transport">Edit Transport</string>
<string name="main">Main</string>
<!-- shown if a QR code was scanned that can be used as a transport -->
<string name="confirm_add_transport">Add this transport?</string>
<string name="invalid_transport_qr">The scanned QR code does not contain a valid transport.</string>