From 0f8e7214afe949adf51eb39444d6f5053383a305 Mon Sep 17 00:00:00 2001 From: Brandon Maxwell Date: Thu, 3 Dec 2015 13:52:20 -0800 Subject: Creating Compatibility class for CallAudioState Bug: 25776171 Change-Id: Ia72b3d4a30783592f8894e0eaa890be7c60743ea --- .../dialer/compat/CallAudioStateCompat.java | 331 +++++++++++++++++++++ .../dialer/voicemail/VoicemailAudioManager.java | 55 ++-- 2 files changed, 359 insertions(+), 27 deletions(-) create mode 100644 src/com/android/dialer/compat/CallAudioStateCompat.java (limited to 'src/com') diff --git a/src/com/android/dialer/compat/CallAudioStateCompat.java b/src/com/android/dialer/compat/CallAudioStateCompat.java new file mode 100644 index 000000000..51009d042 --- /dev/null +++ b/src/com/android/dialer/compat/CallAudioStateCompat.java @@ -0,0 +1,331 @@ +/* + * 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.compat; + +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.telecom.CallAudioState; + +import com.android.contacts.common.compat.SdkVersionOverride; + +import java.util.Locale; + +/** + * Compatibility class for {@link CallAudioState} + */ +public class CallAudioStateCompat { + + /** + * Direct the audio stream through the device's earpiece. + */ + public static final int ROUTE_EARPIECE = CallAudioState.ROUTE_EARPIECE; + + /** + * Direct the audio stream through Bluetooth. + */ + public static final int ROUTE_BLUETOOTH = CallAudioState.ROUTE_BLUETOOTH; + + /** + * Direct the audio stream through a wired headset. + */ + public static final int ROUTE_WIRED_HEADSET = CallAudioState.ROUTE_WIRED_HEADSET; + + /** + * Direct the audio stream through the device's speakerphone. + */ + public static final int ROUTE_SPEAKER = CallAudioState.ROUTE_SPEAKER; + + /** + * Direct the audio stream through the device's earpiece or wired headset if one is connected. + */ + public static final int ROUTE_WIRED_OR_EARPIECE = CallAudioState.ROUTE_WIRED_OR_EARPIECE; + + private final CallAudioStateImpl mCallAudioState; + + /** + * Constructor for a {@link CallAudioStateCompat} object. + * + * @param muted {@code true} if the call is muted, {@code false} otherwise. + * @param route The current audio route being used. Allowed values: {@link #ROUTE_EARPIECE} + * {@link #ROUTE_BLUETOOTH} {@link #ROUTE_WIRED_HEADSET} {@link #ROUTE_SPEAKER} + * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a + * bitwise combination of the following values: {@link #ROUTE_EARPIECE} {@link #ROUTE_BLUETOOTH} + * {@link #ROUTE_WIRED_HEADSET} {@link #ROUTE_SPEAKER} + */ + public CallAudioStateCompat(boolean muted, int route, int supportedRouteMask) { + if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) + < Build.VERSION_CODES.M) { + mCallAudioState = new CallAudioStateBase(muted, route, supportedRouteMask); + } else { + mCallAudioState = new CallAudioStateMarshmallow(muted, route, supportedRouteMask); + } + } + + /** + * @return {@code true} if the call is muted, {@code false} otherwise. + */ + public boolean isMuted() { + return mCallAudioState.isMuted(); + } + + /** + * @return The current audio route being used. + */ + public int getRoute() { + return mCallAudioState.getRoute(); + } + + /** + * @return Bit mask of all routes supported by this call. + */ + public int getSupportedRouteMask() { + return mCallAudioState.getSupportedRouteMask(); + } + + /** + * Converts the provided audio route into a human readable string representation. + * + * @param route to convert into a string. + * @return String representation of the provided audio route. + */ + public static String audioRouteToString(int route) { + if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) + < Build.VERSION_CODES.M) { + return CallAudioStateBase.audioRouteToString(route); + } + return CallAudioStateMarshmallow.audioRouteToString(route); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + CallAudioStateCompat that = (CallAudioStateCompat) o; + + return mCallAudioState.equals(that.mCallAudioState); + + } + + @Override + public int hashCode() { + return mCallAudioState.hashCode(); + } + + @Override + public String toString() { + return mCallAudioState.toString(); + } + + private interface CallAudioStateImpl { + boolean isMuted(); + int getRoute(); + int getSupportedRouteMask(); + } + + /** + * CallAudioStateImpl to use if the Sdk version is lower than + * {@link android.os.Build.VERSION_CODES.M} + * + * Coped from {@link android.telecom.CallAudioState} + * + * Encapsulates the telecom audio state, including the current audio routing, supported audio + * routing and mute. + */ + private static class CallAudioStateBase implements CallAudioStateImpl, Parcelable { + + /** + * Bit mask of all possible audio routes. + */ + private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET + | ROUTE_SPEAKER; + + private final boolean isMuted; + private final int route; + private final int supportedRouteMask; + + /** + * Constructor for a {@link CallAudioStateBase} object. + * + * @param muted {@code true} if the call is muted, {@code false} otherwise. + * @param route The current audio route being used. Allowed values: {@link #ROUTE_EARPIECE} + * {@link #ROUTE_BLUETOOTH}, {@link #ROUTE_WIRED_HEADSET}, {@link #ROUTE_SPEAKER} + * @param supportedRouteMask Bit mask of all routes supported by this call. This should be a + * bitwise combination of the following values: {@link #ROUTE_EARPIECE}, + * {@link #ROUTE_BLUETOOTH}, {@link #ROUTE_WIRED_HEADSET}, {@link #ROUTE_SPEAKER} + */ + public CallAudioStateBase(boolean muted, int route, int supportedRouteMask) { + this.isMuted = muted; + this.route = route; + this.supportedRouteMask = supportedRouteMask; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (!(obj instanceof CallAudioStateBase)) { + return false; + } + CallAudioStateBase state = (CallAudioStateBase) obj; + return isMuted() == state.isMuted() && getRoute() == state.getRoute() && + getSupportedRouteMask() == state.getSupportedRouteMask(); + } + + @Override + public String toString() { + return String.format(Locale.US, + "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]", + isMuted, + audioRouteToString(route), + audioRouteToString(supportedRouteMask)); + } + + /** + * @return {@code true} if the call is muted, {@code false} otherwise. + */ + @Override + public boolean isMuted() { + return isMuted; + } + + /** + * @return The current audio route being used. + */ + public int getRoute() { + return route; + } + + /** + * @return Bit mask of all routes supported by this call. + */ + public int getSupportedRouteMask() { + return supportedRouteMask; + } + + /** + * Converts the provided audio route into a human readable string representation. + * + * @param route to convert into a string. + * @return String representation of the provided audio route. + */ + public static String audioRouteToString(int route) { + if (route == 0 || (route & ~ROUTE_ALL) != 0x0) { + return "UNKNOWN"; + } + + StringBuffer buffer = new StringBuffer(); + if ((route & ROUTE_EARPIECE) == ROUTE_EARPIECE) { + listAppend(buffer, "EARPIECE"); + } + if ((route & ROUTE_BLUETOOTH) == ROUTE_BLUETOOTH) { + listAppend(buffer, "BLUETOOTH"); + } + if ((route & ROUTE_WIRED_HEADSET) == ROUTE_WIRED_HEADSET) { + listAppend(buffer, "WIRED_HEADSET"); + } + if ((route & ROUTE_SPEAKER) == ROUTE_SPEAKER) { + listAppend(buffer, "SPEAKER"); + } + + return buffer.toString(); + } + + /** + * Responsible for creating AudioState objects for deserialized Parcels. + */ + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + @Override + public CallAudioStateBase createFromParcel(Parcel source) { + boolean isMuted = source.readByte() == 0 ? false : true; + int route = source.readInt(); + int supportedRouteMask = source.readInt(); + return new CallAudioStateBase(isMuted, route, supportedRouteMask); + } + + @Override + public CallAudioStateBase[] newArray(int size) { + return new CallAudioStateBase[size]; + } + }; + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + /** + * Writes AudioState object into a serializeable Parcel. + */ + @Override + public void writeToParcel(Parcel destination, int flags) { + destination.writeByte((byte) (isMuted ? 1 : 0)); + destination.writeInt(route); + destination.writeInt(supportedRouteMask); + } + + private static void listAppend(StringBuffer buffer, String str) { + if (buffer.length() > 0) { + buffer.append(", "); + } + buffer.append(str); + } + } + + /** + * CallAudioStateImpl to use if the Sdk version is at least + * {@link android.os.Build.VERSION_CODES.M} + */ + private static class CallAudioStateMarshmallow implements CallAudioStateImpl { + + private final CallAudioState mCallAudioStateDelegate; + + public CallAudioStateMarshmallow(boolean muted, int route, int supportedRouteMask) { + mCallAudioStateDelegate = new CallAudioState(muted, route, supportedRouteMask); + } + + @Override + public boolean isMuted() { + return mCallAudioStateDelegate.isMuted(); + } + + @Override + public int getRoute() { + return mCallAudioStateDelegate.getRoute(); + } + + @Override + public int getSupportedRouteMask() { + return mCallAudioStateDelegate.getSupportedRouteMask(); + } + + public static String audioRouteToString(int route) { + return CallAudioState.audioRouteToString(route); + } + } +} diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java index 267eeca09..712b20bf1 100644 --- a/src/com/android/dialer/voicemail/VoicemailAudioManager.java +++ b/src/com/android/dialer/voicemail/VoicemailAudioManager.java @@ -19,10 +19,10 @@ 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.Objects; +import com.android.dialer.compat.CallAudioStateCompat; + import java.util.concurrent.RejectedExecutionException; /** @@ -38,7 +38,7 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener, private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter; private WiredHeadsetManager mWiredHeadsetManager; private boolean mWasSpeakerOn; - private CallAudioState mCallAudioState; + private CallAudioStateCompat mCallAudioState; public VoicemailAudioManager(Context context, VoicemailPlaybackPresenter voicemailPlaybackPresenter) { @@ -82,25 +82,26 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener, int newRoute = mCallAudioState.getRoute(); // start out with existing route if (newIsPluggedIn) { - newRoute = CallAudioState.ROUTE_WIRED_HEADSET; + newRoute = CallAudioStateCompat.ROUTE_WIRED_HEADSET; } else { if (mWasSpeakerOn) { - newRoute = CallAudioState.ROUTE_SPEAKER; + newRoute = CallAudioStateCompat.ROUTE_SPEAKER; } else { - newRoute = CallAudioState.ROUTE_EARPIECE; + newRoute = CallAudioStateCompat.ROUTE_EARPIECE; } } - mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioState.ROUTE_SPEAKER); + mVoicemailPlaybackPresenter.setSpeakerphoneOn(newRoute == CallAudioStateCompat.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())); + new CallAudioStateCompat(false /* muted */, newRoute, calculateSupportedRoutes())); } public void setSpeakerphoneOn(boolean on) { - setAudioRoute(on ? CallAudioState.ROUTE_SPEAKER : CallAudioState.ROUTE_WIRED_OR_EARPIECE); + setAudioRoute(on ? CallAudioStateCompat.ROUTE_SPEAKER + : CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE); } public boolean isWiredHeadsetPluggedIn() { @@ -119,10 +120,10 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener, /** * Change the audio route, for example from earpiece to speakerphone. * - * @param route The new audio route to use. See {@link CallAudioState}. + * @param route The new audio route to use. See {@link CallAudioStateCompat}. */ void setAudioRoute(int route) { - Log.v(TAG, "setAudioRoute, route: " + CallAudioState.audioRouteToString(route)); + Log.v(TAG, "setAudioRoute, route: " + CallAudioStateCompat.audioRouteToString(route)); // Change ROUTE_WIRED_OR_EARPIECE to a single entry. int newRoute = selectWiredOrEarpiece(route, mCallAudioState.getSupportedRouteMask()); @@ -136,25 +137,25 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener, 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, + mWasSpeakerOn = newRoute == CallAudioStateCompat.ROUTE_SPEAKER; + setSystemAudioState(new CallAudioStateCompat(false /* muted */, newRoute, mCallAudioState.getSupportedRouteMask())); } } - private CallAudioState getInitialAudioState() { + private CallAudioStateCompat getInitialAudioState() { int supportedRouteMask = calculateSupportedRoutes(); - int route = selectWiredOrEarpiece(CallAudioState.ROUTE_WIRED_OR_EARPIECE, + int route = selectWiredOrEarpiece(CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE, supportedRouteMask); - return new CallAudioState(false /* muted */, route, supportedRouteMask); + return new CallAudioStateCompat(false /* muted */, route, supportedRouteMask); } private int calculateSupportedRoutes() { - int routeMask = CallAudioState.ROUTE_SPEAKER; + int routeMask = CallAudioStateCompat.ROUTE_SPEAKER; if (mWiredHeadsetManager.isPluggedIn()) { - routeMask |= CallAudioState.ROUTE_WIRED_HEADSET; + routeMask |= CallAudioStateCompat.ROUTE_WIRED_HEADSET; } else { - routeMask |= CallAudioState.ROUTE_EARPIECE; + routeMask |= CallAudioStateCompat.ROUTE_EARPIECE; } return routeMask; } @@ -163,29 +164,29 @@ final class VoicemailAudioManager implements OnAudioFocusChangeListener, // 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 == CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE) { + route = CallAudioStateCompat.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; + route = CallAudioStateCompat.ROUTE_EARPIECE; } } return route; } - private void setSystemAudioState(CallAudioState callAudioState) { - CallAudioState oldAudioState = mCallAudioState; + private void setSystemAudioState(CallAudioStateCompat callAudioState) { + CallAudioStateCompat oldAudioState = mCallAudioState; mCallAudioState = callAudioState; Log.i(TAG, "setSystemAudioState: changing from " + oldAudioState + " to " + mCallAudioState); // Audio route. - if (mCallAudioState.getRoute() == CallAudioState.ROUTE_SPEAKER) { + if (mCallAudioState.getRoute() == CallAudioStateCompat.ROUTE_SPEAKER) { turnOnSpeaker(true); - } else if (mCallAudioState.getRoute() == CallAudioState.ROUTE_EARPIECE || - mCallAudioState.getRoute() == CallAudioState.ROUTE_WIRED_HEADSET) { + } else if (mCallAudioState.getRoute() == CallAudioStateCompat.ROUTE_EARPIECE || + mCallAudioState.getRoute() == CallAudioStateCompat.ROUTE_WIRED_HEADSET) { // Just handle turning off the speaker, the system will handle switching between wired // headset and earpiece. turnOnSpeaker(false); -- cgit v1.2.3