Removed READ_PHONE_STATE permission and use AudioManager.MODE_IN_CALL instead.

This commit is contained in:
Dennis Guse
2019-12-03 20:54:33 +01:00
parent d1a459efc2
commit 303abac980
2 changed files with 43 additions and 77 deletions
-2
View File
@@ -163,8 +163,6 @@ limitations under the License.
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- Permission to read phone state so voice announcement will not interrupt ongoing calls -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> </manifest>
@@ -21,8 +21,6 @@ import android.media.AudioManager;
import android.speech.tts.TextToSpeech; import android.speech.tts.TextToSpeech;
import android.speech.tts.TextToSpeech.OnInitListener; import android.speech.tts.TextToSpeech.OnInitListener;
import android.speech.tts.UtteranceProgressListener; import android.speech.tts.UtteranceProgressListener;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log; import android.util.Log;
import java.util.Locale; import java.util.Locale;
@@ -51,10 +49,39 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
private final Context context; private final Context context;
private final AudioManager audioManager; private final AudioManager audioManager;
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
Log.d(TAG, "Audio focus changed to " + focusChange);
boolean stop = false;
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
stop = false;
break;
case AudioManager.AUDIOFOCUS_LOSS:
stop = true;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
stop = true;
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
stop = true;
break;
}
if (stop && tts != null && tts.isSpeaking()) {
tts.stop();
Log.i(TAG, "Aborting current tts due to focus change " + focusChange);
}
}
};
private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() { private final UtteranceProgressListener utteranceListener = new UtteranceProgressListener() {
@Override @Override
public void onStart(String utteranceId) { public void onStart(String utteranceId) {
int result = audioManager.requestAudioFocus(null, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); int result = audioManager.requestAudioFocus(audioFocusChangeListener, TextToSpeech.Engine.DEFAULT_STREAM, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
Log.w(TAG, "Failed to request audio focus."); Log.w(TAG, "Failed to request audio focus.");
} }
@@ -70,32 +97,16 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
@Override @Override
public void onError(String utteranceId) { public void onError(String utteranceId) {
Log.e(TAG, "An error occurred for utteranceId " + utteranceId);
} }
}; };
private TextToSpeech tts; private TextToSpeech tts;
// Response from TTS after its initialization // Response from TTS after its initialization
private int initStatus = TextToSpeech.ERROR; private int ttsInitStatus = TextToSpeech.ERROR;
private boolean ttsReady = false; private boolean ttsReady = false;
// True if speech is allowed
private boolean speechAllowed;
/**
* Listener which updates {@link #speechAllowed} when the phone state changes.
*/
private final PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
speechAllowed = state == TelephonyManager.CALL_STATE_IDLE;
if (!speechAllowed && tts != null && tts.isSpeaking()) {
// If we're already speaking, stop it.
tts.stop();
}
}
};
AnnouncementPeriodicTask(Context context) { AnnouncementPeriodicTask(Context context) {
this.context = context; this.context = context;
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
@@ -103,16 +114,17 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
@Override @Override
public void start() { public void start() {
Log.d(TAG, "Start");
if (tts == null) { if (tts == null) {
tts = newTextToSpeech(context, new OnInitListener() { tts = new TextToSpeech(context, new OnInitListener() {
@Override @Override
public void onInit(int status) { public void onInit(int status) {
initStatus = status; Log.i(TAG, "TextToSpeech initialized with status " + status);
ttsInitStatus = status;
} }
}); });
} }
speechAllowed = true;
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
} }
@Override @Override
@@ -137,7 +149,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
synchronized (this) { synchronized (this) {
if (!ttsReady) { if (!ttsReady) {
ttsReady = initStatus == TextToSpeech.SUCCESS; ttsReady = ttsInitStatus == TextToSpeech.SUCCESS;
if (ttsReady) { if (ttsReady) {
onTtsReady(); onTtsReady();
} }
@@ -148,7 +160,7 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
} }
} }
if (!speechAllowed) { if (audioManager.getMode() == AudioManager.MODE_IN_CALL || audioManager.getMode() == AudioManager.MODE_IN_COMMUNICATION) {
Log.i(TAG, "Speech is not allowed at this time."); Log.i(TAG, "Speech is not allowed at this time.");
return; return;
} }
@@ -157,21 +169,18 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
@Override @Override
public void shutdown() { public void shutdown() {
listenToPhoneState(phoneStateListener, PhoneStateListener.LISTEN_NONE);
if (tts != null) { if (tts != null) {
tts.shutdown(); tts.shutdown();
tts = null; tts = null;
} }
} }
/**
* Called when TTS is ready.
*/
private void onTtsReady() { private void onTtsReady() {
Locale locale = Locale.getDefault(); Locale locale = Locale.getDefault();
int languageAvailability = tts.isLanguageAvailable(locale); int languageAvailability = tts.isLanguageAvailable(locale);
if (languageAvailability == TextToSpeech.LANG_MISSING_DATA if (languageAvailability == TextToSpeech.LANG_MISSING_DATA || languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
|| languageAvailability == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.w(TAG, "Default locale not available, use English."); Log.w(TAG, "Default locale not available, use English.");
locale = Locale.ENGLISH; locale = Locale.ENGLISH;
/* /*
@@ -187,34 +196,11 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
tts.setOnUtteranceProgressListener(utteranceListener); tts.setOnUtteranceProgressListener(utteranceListener);
} }
/**
* Speaks the announcement.
*
* @param announcement the announcement
*/
private void speakAnnouncement(String announcement) { private void speakAnnouncement(String announcement) {
/* // We don't care about the utterance id. It is supplied here to force onUtteranceCompleted to be called.
* We don't care about the utterance id. It is supplied here to force
* onUtteranceCompleted to be called.
*/
tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used"); tts.speak(announcement, TextToSpeech.QUEUE_FLUSH, null, "not used");
} }
/**
* Create a new {@link TextToSpeech}.
*
* @param aContext a context
* @param onInitListener an on init listener
*/
private TextToSpeech newTextToSpeech(Context aContext, OnInitListener onInitListener) {
return new TextToSpeech(aContext, onInitListener);
}
/**
* Gets the announcement.
*
* @param tripStatistics the trip statistics
*/
private String getAnnouncement(TripStatistics tripStatistics) { private String getAnnouncement(TripStatistics tripStatistics) {
boolean metricUnits = PreferencesUtils.isMetricUnits(context); boolean metricUnits = PreferencesUtils.isMetricUnits(context);
boolean reportSpeed = PreferencesUtils.isReportSpeed(context); boolean reportSpeed = PreferencesUtils.isReportSpeed(context);
@@ -248,24 +234,6 @@ public class AnnouncementPeriodicTask implements PeriodicTask {
getAnnounceTime(tripStatistics.getMovingTime()), rate); getAnnounceTime(tripStatistics.getMovingTime()), rate);
} }
/**
* Listens to phone state.
*
* @param listener the listener
* @param events the interested events
*/
private void listenToPhoneState(PhoneStateListener listener, int events) {
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (telephony != null) {
telephony.listen(listener, events);
}
}
/**
* Gets the announce time.
*
* @param time the time
*/
private String getAnnounceTime(long time) { private String getAnnounceTime(long time) {
int[] parts = StringUtils.getTimeParts(time); int[] parts = StringUtils.getTimeParts(time);
String seconds = context.getResources() String seconds = context.getResources()