Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 13308a39 authored by Brandon Maxwell's avatar Brandon Maxwell Committed by Android (Google) Code Review
Browse files

Merge "Creating Compatibility class for CallAudioState" into ub-contactsdialer-b-dev

parents 736c6bde 0f8e7214
Loading
Loading
Loading
Loading
+331 −0
Original line number Diff line number Diff line
/*
 * 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<CallAudioStateBase> CREATOR =
                new Parcelable.Creator<CallAudioStateBase>() {

                    @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);
        }
    }
}
+28 −27
Original line number Diff line number Diff line
@@ -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);