diff --git a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java index 2bec92c20..55ff54bbe 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackListActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackListActivity.java @@ -40,6 +40,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import com.google.android.material.button.MaterialButton; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; @@ -47,6 +48,7 @@ import java.util.Objects; import de.dennisguse.opentracks.data.ContentProviderUtils; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.databinding.TrackListBinding; +import de.dennisguse.opentracks.services.MissingPermissionException; import de.dennisguse.opentracks.services.RecordingStatus; import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.services.TrackRecordingServiceConnection; @@ -159,7 +161,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co if (gpsStatusValue.isGpsStarted()) { recordingStatusConnection.stopService(this); } 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 Log.i(TAG, "Starting recording"); updateGpsMenuItem(false, true); - TrackRecordingServiceConnection.executeForeground(this, (service, connection) -> { + startSensorsOrRecording((service, connection) -> { Track.Id trackId = service.startNewTrack(); Intent newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class); @@ -192,7 +194,7 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co } // Recording -> Stop - ActivityUtils.vibrate(this, 1000); + ActivityUtils.vibrate(this, Duration.ofSeconds(1)); updateGpsMenuItem(false, false); recordingStatusConnection.stopRecording(TrackListActivity.this); viewBinding.trackListFabAction.setImageResource(R.drawable.ic_baseline_record_24); @@ -445,4 +447,12 @@ public class TrackListActivity extends AbstractTrackDeleteActivity implements Co setFloatButton(); 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(); + } + } } diff --git a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java index 3390484d1..c90217fce 100644 --- a/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java +++ b/src/main/java/de/dennisguse/opentracks/TrackRecordingActivity.java @@ -24,6 +24,7 @@ import androidx.viewpager2.adapter.FragmentStateAdapter; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayoutMediator; +import java.time.Duration; import java.util.List; 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.setBackgroundColor(ContextCompat.getColor(this, R.color.opentracks)); viewBinding.trackRecordingFabAction.setOnLongClickListener((view) -> { - ActivityUtils.vibrate(this, 1000); + ActivityUtils.vibrate(this, Duration.ofSeconds(1)); trackRecordingServiceConnection.stopRecording(TrackRecordingActivity.this); Intent newIntent = IntentUtils.newIntent(TrackRecordingActivity.this, TrackStoppedActivity.class) .putExtra(TrackStoppedActivity.EXTRA_TRACK_ID, trackId); diff --git a/src/main/java/de/dennisguse/opentracks/services/MissingPermissionException.java b/src/main/java/de/dennisguse/opentracks/services/MissingPermissionException.java new file mode 100644 index 000000000..26d1ecb58 --- /dev/null +++ b/src/main/java/de/dennisguse/opentracks/services/MissingPermissionException.java @@ -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; + } +} diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java index ad2e62722..d17115087 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingService.java @@ -20,7 +20,6 @@ import android.app.Service; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ServiceInfo; -import android.os.Build; import android.os.Handler; import android.os.Looper; 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.TrackPointCreator; import de.dennisguse.opentracks.settings.PreferencesUtils; -import de.dennisguse.opentracks.util.PermissionRequester; import de.dennisguse.opentracks.util.SystemUtils; 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() { if (isSensorStarted()) { Log.i(TAG, "sensors already started; skipping"); - return; } Log.i(TAG, "startSensors"); wakeLock = SystemUtils.acquireWakeLock(this, wakeLock); 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); } diff --git a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java index 26505d5ab..7adf72a2b 100644 --- a/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java +++ b/src/main/java/de/dennisguse/opentracks/services/TrackRecordingServiceConnection.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Build; import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; @@ -29,6 +30,7 @@ import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import de.dennisguse.opentracks.BuildConfig; +import de.dennisguse.opentracks.util.PermissionRequester; /** * Wrapper for the track recording service. @@ -157,6 +159,12 @@ public class TrackRecordingServiceConnection { } 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.onConnected(service, connection); connection.unbind(context); diff --git a/src/main/java/de/dennisguse/opentracks/ui/util/ActivityUtils.java b/src/main/java/de/dennisguse/opentracks/ui/util/ActivityUtils.java index cb1ed6c9a..5d3d90817 100644 --- a/src/main/java/de/dennisguse/opentracks/ui/util/ActivityUtils.java +++ b/src/main/java/de/dennisguse/opentracks/ui/util/ActivityUtils.java @@ -15,6 +15,8 @@ import android.view.MenuItem; import androidx.annotation.NonNull; import androidx.appcompat.widget.SearchView; +import java.time.Duration; + public class ActivityUtils { private static final String TAG = ActivityUtils.class.getSimpleName(); @@ -32,12 +34,12 @@ public class ActivityUtils { 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); 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 { - vibrator.vibrate(milliseconds); + vibrator.vibrate(duration.toMillis()); } }