diff options
Diffstat (limited to 'src/com/android/dialer/voicemail/VoicemailAudioManager.java')
-rw-r--r-- | src/com/android/dialer/voicemail/VoicemailAudioManager.java | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java new file mode 100644 index 000000000..fe6cf5f45 --- /dev/null +++ b/src/com/android/dialer/voicemail/VoicemailAudioManager.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * 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 com.android.dialer.voicemail; + +import android.content.Context; +import android.media.AudioManager; +import android.media.AudioManager.OnAudioFocusChangeListener; +import android.telecom.CallAudioState; +import android.util.Log; + +import java.util.concurrent.RejectedExecutionException; + +/** + * This class manages all audio changes for voicemail playback. + */ +final class VoicemailAudioManager implements OnAudioFocusChangeListener, + WiredHeadsetManager.Listener { + private static final String TAG = VoicemailAudioManager.class.getSimpleName(); + + public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL; + + private AudioManager mAudioManager; + private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; + private WiredHeadsetManager mWiredHeadsetManager; + private boolean mWasSpeakerOn; + private CallAudioState mCallAudioState; + + public VoicemailAudioManager(Context context, + VoicemailPlaybackPresenter voicemailPlaybackPresenter) { + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mVoicemailPlaybackPresenter = voicemailPlaybackPresenter; + mWiredHeadsetManager = new WiredHeadsetManager(context); + mWiredHeadsetManager.setListener(this); + + mCallAudioState = getInitialAudioState(); + Log.i(TAG, "Initial audioState = " + mCallAudioState); + } + + public void requestAudioFocus() { + int result = mAudioManager.requestAudioFocus( + this, + PLAYBACK_STREAM, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { + throw new RejectedExecutionException("Could not capture audio focus."); + } + } + + public void abandonAudioFocus() { + mAudioManager.abandonAudioFocus(this); + } + + @Override + public void onAudioFocusChange(int focusChange) { + Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange); + mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN); + } + + @Override + public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) { + Log.i(TAG, "wired headset was plugged in changed: " + oldIsPluggedIn + + " -> "+ newIsPluggedIn); + + if (oldIsPluggedIn == newIsPluggedIn) { + return; + } + + int newRoute = mCallAudioState.getRoute(); // start out with existing route + if (newIsPluggedIn) { + newRoute = CallAudioState.ROUTE_WIRED_HEADSET; + } else { + if (mWasSpeakerOn) { + newRoute = CallAudioState.ROUTE_SPEAKER; + } else { + newRoute = CallAudioState.ROUTE_EARPIECE; + } + } + + mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER); + + // We need to call this every time even if we do not change the route because the supported + // routes changed either to include or not include WIRED_HEADSET. + setSystemAudioState( + new CallAudioState(false /* muted */, newRoute, calculateSupportedRoutes())); + } + + public void setSpeakerphoneOn(boolean on) { + setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE); + } + + public boolean isWiredHeadsetPluggedIn() { + return mWiredHeadsetManager.isPluggedIn(); + } + + public void registerReceivers() { + // Receivers is plural because we expect to add bluetooth support. + mWiredHeadsetManager.registerReceiver(); + } + + public void unregisterReceivers() { + mWiredHeadsetManager.unregisterReceiver(); + } + + /** + * Change the audio route, for example from earpiece to speakerphone. + * + * @param route The new audio route to use. See {@link CallAudioState}. + */ + void setAudioRoute(int route) { + Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route)); + + // Change ROUTE_WIRED_OR_EARPIECE to a single entry. + int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask()); + + // If route is unsupported, do nothing. + if ((mCallAudioState.getSupportedRouteMask() | newRoute) == 0) { + Log.w(TAG, "Asking to set to a route that is unsupported: " + newRoute); + return; + } + + if (mCallAudioState.getRoute() != newRoute) { + // Remember the new speaker state so it can be restored when the user plugs and unplugs + // a headset. + mWasSpeakerOn = newRoute == CallAudioState.ROUTE_SPEAKER; + setSystemAudioState(new CallAudioState(false /* muted */, newRoute, + mCallAudioState.getSupportedRouteMask())); + } + } + + private CallAudioState getInitialAudioState() { + int supportedRouteMask = calculateSupportedRoutes(); + int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE, + supportedRouteMask); + return new CallAudioState(false /* muted */, route, supportedRouteMask); + } + + private int calculateSupportedRoutes() { + int routeMask = CallAudioState.ROUTE_SPEAKER; + if (mWiredHeadsetManager.isPluggedIn()) { + routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; + } else { + routeMask |= CallAudioState.ROUTE_EARPIECE; + } + return routeMask; + } + + private int selectWiredOrEarpiece(int route, int supportedRouteMask) { + // Since they are mutually exclusive and one is ALWAYS valid, we allow a special input of + // ROUTE_WIRED_OR_EARPIECE so that callers don't have to make a call to check which is + // supported before calling setAudioRoute. + if (route == CallAudioState.ROUTE_WIRED_OR_EARPIECE) { + route = CallAudioState.ROUTE_WIRED_OR_EARPIECE & supportedRouteMask; + if (route == 0) { + Log.wtf(TAG, "One of wired headset or earpiece should always be valid."); + // assume earpiece in this case. + route = CallAudioState.ROUTE_EARPIECE; + } + } + return route; + } + + private void setSystemAudioState(CallAudioState callAudioState) { + CallAudioState oldAudioState = mCallAudioState; + mCallAudioState = callAudioState; + + Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to " + + mCallAudioState); + + // Audio route. + if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) { + turnOnSpeaker(true); + } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE || + mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) { + // Just handle turning off the speaker, the system will handle switching between wired + // headset and earpiece. + turnOnSpeaker(false); + } + } + + private void turnOnSpeaker(boolean on) { + if (mAudioManager.isSpeakerphoneOn() != on) { + Log.i(TAG, "turning speaker phone on: " + on); + mAudioManager.setSpeakerphoneOn(on); + } + } +} |