forked from upstream-mirrors/OpenTracks
Bugfix: handle missing permissions properly.
Affected only SDK >= 33. Fixes #1781.
This commit is contained in:
@@ -40,6 +40,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
|||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -47,6 +48,7 @@ import java.util.Objects;
|
|||||||
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
import de.dennisguse.opentracks.data.ContentProviderUtils;
|
||||||
import de.dennisguse.opentracks.data.models.Track;
|
import de.dennisguse.opentracks.data.models.Track;
|
||||||
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
import de.dennisguse.opentracks.databinding.TrackListBinding;
|
||||||
|
import de.dennisguse.opentracks.services.MissingPermissionException;
|
||||||
import de.dennisguse.opentracks.services.RecordingStatus;
|
import de.dennisguse.opentracks.services.RecordingStatus;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingService;
|
import de.dennisguse.opentracks.services.TrackRecordingService;
|
||||||
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
import de.dennisguse.opentracks.services.TrackRecordingServiceConnection;
|
||||||
@@ -159,7 +161,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
if (gpsStatusValue.isGpsStarted()) {
|
if (gpsStatusValue.isGpsStarted()) {
|
||||||
recordingStatusConnection.stopService(this);
|
recordingStatusConnection.stopService(this);
|
||||||
} else {
|
} else {
|
||||||
TrackRecordingServiceConnection.executeForeground(this, (service, connection) -> service.tryStartSensors());
|
startSensorsOrRecording((service, connection) -> service.tryStartSensors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -178,7 +180,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
// Not Recording -> Recording
|
// Not Recording -> Recording
|
||||||
Log.i(TAG, "Starting recording");
|
Log.i(TAG, "Starting recording");
|
||||||
updateGpsMenuItem(false, true);
|
updateGpsMenuItem(false, true);
|
||||||
TrackRecordingServiceConnection.executeForeground(this, (service, connection) -> {
|
startSensorsOrRecording((service, connection) -> {
|
||||||
Track.Id trackId = service.startNewTrack();
|
Track.Id trackId = service.startNewTrack();
|
||||||
|
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class);
|
||||||
@@ -192,7 +194,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recording -> Stop
|
// Recording -> Stop
|
||||||
ActivityUtils.vibrate(this, 1000);
|
ActivityUtils.vibrate(this, Duration.ofSeconds(1));
|
||||||
updateGpsMenuItem(false, false);
|
updateGpsMenuItem(false, false);
|
||||||
recordingStatusConnection.stopRecording(TrackListActivity.this);
|
recordingStatusConnection.stopRecording(TrackListActivity.this);
|
||||||
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24);
|
||||||
@@ -445,4 +447,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co
|
|||||||
setFloatButton();
|
setFloatButton();
|
||||||
adapter.updateRecordingStatus(recordingStatus);
|
adapter.updateRecordingStatus(recordingStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startSensorsOrRecording(TrackRecordingServiceConnection.Callback callback) {
|
||||||
|
try {
|
||||||
|
TrackRecordingServiceConnection.executeForeground(this, callback);
|
||||||
|
} catch (MissingPermissionException e) {
|
||||||
|
Toast.makeText(this, R.string.permission_recording_failed, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
|
|||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.android.material.tabs.TabLayoutMediator;
|
import com.google.android.material.tabs.TabLayoutMediator;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.chart.ChartFragment;
|
import de.dennisguse.opentracks.chart.ChartFragment;
|
||||||
@@ -136,7 +137,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
|||||||
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks));
|
viewBinding.trackRecordingFabAction.setBackgroundTintList(ContextCompat.getColorStateList(this, R.color.opentracks));
|
||||||
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks));
|
viewBinding.trackRecordingFabAction.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks));
|
||||||
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> {
|
||||||
ActivityUtils.vibrate(this, 1000);
|
ActivityUtils.vibrate(this, Duration.ofSeconds(1));
|
||||||
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this);
|
||||||
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class)
|
||||||
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
.putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package de.dennisguse.opentracks.services;
|
||||||
|
|
||||||
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||||
|
|
||||||
|
public class MissingPermissionException extends RuntimeException {
|
||||||
|
|
||||||
|
private final PermissionRequester permissionRequester;
|
||||||
|
|
||||||
|
public MissingPermissionException(PermissionRequester permissionRequester) {
|
||||||
|
this.permissionRequester = permissionRequester;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,6 @@ import android.app.Service;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ServiceInfo;
|
import android.content.pm.ServiceInfo;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.PowerManager.WakeLock;
|
import android.os.PowerManager.WakeLock;
|
||||||
@@ -46,7 +45,6 @@ import de.dennisguse.opentracks.services.announcement.VoiceAnnouncementManager;
|
|||||||
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
import de.dennisguse.opentracks.services.handlers.GpsStatusValue;
|
||||||
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
import de.dennisguse.opentracks.services.handlers.TrackPointCreator;
|
||||||
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
import de.dennisguse.opentracks.settings.PreferencesUtils;
|
||||||
import de.dennisguse.opentracks.util.PermissionRequester;
|
|
||||||
import de.dennisguse.opentracks.util.SystemUtils;
|
import de.dennisguse.opentracks.util.SystemUtils;
|
||||||
|
|
||||||
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
|
public class TrackRecordingService extends Service implements TrackPointCreator.Callback, SharedPreferences.OnSharedPreferenceChangeListener, TrackRecordingManager.IdleObserver {
|
||||||
@@ -207,18 +205,11 @@ public class TrackRecordingService extends Service implements TrackPointCreator.
|
|||||||
private synchronized void startSensors() {
|
private synchronized void startSensors() {
|
||||||
if (isSensorStarted()) {
|
if (isSensorStarted()) {
|
||||||
Log.i(TAG, "sensors already started; skipping");
|
Log.i(TAG, "sensors already started; skipping");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
Log.i(TAG, "startSensors");
|
Log.i(TAG, "startSensors");
|
||||||
wakeLock = SystemUtils.acquireWakeLock(this, wakeLock);
|
wakeLock = SystemUtils.acquireWakeLock(this, wakeLock);
|
||||||
trackPointCreator.start(this, handler);
|
trackPointCreator.start(this, handler);
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
||||||
if (!PermissionRequester.RECORDING.hasPermission(this)) {
|
|
||||||
throw new RuntimeException("Android14: Please grant permissions LOCATION and NEARBY DEVICES (manually)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ServiceCompat.startForeground(this, TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.setGPSonlyStarted(this), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION + ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
ServiceCompat.startForeground(this, TrackRecordingServiceNotificationManager.NOTIFICATION_ID, notificationManager.setGPSonlyStarted(this), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION + ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.IBinder.DeathRecipient;
|
import android.os.IBinder.DeathRecipient;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -29,6 +30,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import de.dennisguse.opentracks.BuildConfig;
|
import de.dennisguse.opentracks.BuildConfig;
|
||||||
|
import de.dennisguse.opentracks.util.PermissionRequester;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for the track recording service.
|
* Wrapper for the track recording service.
|
||||||
@@ -157,6 +159,12 @@ public class TrackRecordingServiceConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void executeForeground(Context context, Callback callback) {
|
public static void executeForeground(Context context, Callback callback) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
if (!PermissionRequester.RECORDING.hasPermission(context)) {
|
||||||
|
throw new MissingPermissionException(PermissionRequester.RECORDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Callback withUnbind = (service, connection) -> {
|
Callback withUnbind = (service, connection) -> {
|
||||||
callback.onConnected(service, connection);
|
callback.onConnected(service, connection);
|
||||||
connection.unbind(context);
|
connection.unbind(context);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import android.view.MenuItem;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.appcompat.widget.SearchView;
|
import androidx.appcompat.widget.SearchView;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
public class ActivityUtils {
|
public class ActivityUtils {
|
||||||
|
|
||||||
private static final String TAG = ActivityUtils.class.getSimpleName();
|
private static final String TAG = ActivityUtils.class.getSimpleName();
|
||||||
@@ -32,12 +34,12 @@ public class ActivityUtils {
|
|||||||
return searchView;
|
return searchView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void vibrate(@NonNull Context context, int milliseconds) {
|
public static void vibrate(@NonNull Context context, Duration duration) {
|
||||||
final Vibrator vibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
|
final Vibrator vibrator = (Vibrator) context.getSystemService(VIBRATOR_SERVICE);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
vibrator.vibrate(VibrationEffect.createOneShot(milliseconds, VibrationEffect.DEFAULT_AMPLITUDE));
|
vibrator.vibrate(VibrationEffect.createOneShot(duration.toMillis(), VibrationEffect.DEFAULT_AMPLITUDE));
|
||||||
} else {
|
} else {
|
||||||
vibrator.vibrate(milliseconds);
|
vibrator.vibrate(duration.toMillis());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user