/* * Copyright 2010 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package de.dennisguse.opentracks.services.announcement; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.mediarouter.media.MediaRouter; import java.time.Duration; import de.dennisguse.opentracks.R; import de.dennisguse.opentracks.data.models.Distance; import de.dennisguse.opentracks.data.models.Track; import de.dennisguse.opentracks.services.TrackRecordingService; import de.dennisguse.opentracks.settings.PreferencesUtils; import de.dennisguse.opentracks.stats.TrackStatistics; /** * Execute a periodic task on a time or distance schedule. * * @author Sandor Dornbush */ public class VoiceAnnouncementManager implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = VoiceAnnouncementManager.class.getSimpleName(); private final TrackRecordingService trackRecordingService; private VoiceAnnouncement voiceAnnouncement; private TrackStatistics trackStatistics; private static final Distance DISTANCE_OFF = Distance.of(Double.MAX_VALUE); private Distance distanceFrequency = DISTANCE_OFF; @NonNull private Distance nextTotalDistance = DISTANCE_OFF; private static final Duration TOTALTIME_OFF = Duration.ofMillis(Long.MAX_VALUE); private Duration totalTimeFrequency = TOTALTIME_OFF; @NonNull private Duration nextTotalTime = TOTALTIME_OFF; public VoiceAnnouncementManager(@NonNull TrackRecordingService trackRecordingService) { this.trackRecordingService = trackRecordingService; PreferencesUtils.registerOnSharedPreferenceChangeListener(this); } public void start(@Nullable TrackStatistics trackStatistics) { voiceAnnouncement = new VoiceAnnouncement(trackRecordingService); voiceAnnouncement.start(); update(trackStatistics); } void update(@Nullable TrackStatistics trackStatistics) { this.trackStatistics = trackStatistics; updateNextDuration(); updateNextTaskDistance(); } public void update(@NonNull Context context, @NonNull Track track) { if (voiceAnnouncement == null) { Log.e(TAG, "Cannot update when in status shutdown."); return; } if (!PreferencesUtils.shouldVoiceAnnouncementOnDeviceSpeaker() && MediaRouter.getInstance(context) .getSelectedRoute() .isDeviceSpeaker()) { Log.i(TAG, "No voice announcement on device speaker."); return; } boolean announce = false; this.trackStatistics = track.getTrackStatistics(); if (trackStatistics.getTotalDistance().greaterThan(nextTotalDistance)) { updateNextTaskDistance(); announce = true; } if (!trackStatistics.getTotalTime().minus(nextTotalTime).isNegative()) { updateNextDuration(); announce = true; } if (announce) { voiceAnnouncement.announce(track); } } public void stop() { PreferencesUtils.unregisterOnSharedPreferenceChangeListener(this); if (voiceAnnouncement != null) { voiceAnnouncement.stop(); voiceAnnouncement = null; } } public void setFrequency(Duration frequency) { this.totalTimeFrequency = frequency; update(this.trackStatistics); } public void setFrequency(Distance frequency) { this.distanceFrequency = frequency; update(this.trackStatistics); } public void updateNextTaskDistance() { if (trackStatistics == null || distanceFrequency.isZero()) { nextTotalDistance = DISTANCE_OFF; } else { Distance distance = trackStatistics.getTotalDistance(); int index = (int) (distance.dividedBy(distanceFrequency)); nextTotalDistance = distanceFrequency.multipliedBy(index + 1); } } private void updateNextDuration() { if (trackStatistics == null || totalTimeFrequency.isZero()) { nextTotalTime = TOTALTIME_OFF; } else { Duration totalTime = trackStatistics.getTotalTime(); Duration intervalMod = Duration.ofMillis(trackStatistics.getTotalTime().toMillis() % totalTimeFrequency.toMillis()); nextTotalTime = totalTime.plus(totalTimeFrequency.minus(intervalMod)); } } @VisibleForTesting @NonNull public Duration getNextTotalTime() { return nextTotalTime; } @VisibleForTesting @NonNull public Distance getNextTotalDistance() { return nextTotalDistance; } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (PreferencesUtils.isKey(R.string.voice_announcement_frequency_key, key)) { setFrequency(PreferencesUtils.getVoiceAnnouncementFrequency()); } if (PreferencesUtils.isKey(new int[]{R.string.voice_announcement_distance_key, R.string.stats_units_key}, key)) { setFrequency(PreferencesUtils.getVoiceAnnouncementDistance()); } } }