19cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien/*
29cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Copyright (C) 2013 The Android Open Source Project
39cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
49cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Licensed under the Apache License, Version 2.0 (the "License");
59cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * you may not use this file except in compliance with the License.
69cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * You may obtain a copy of the License at
79cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
89cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *      http://www.apache.org/licenses/LICENSE-2.0
99cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
109cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Unless required by applicable law or agreed to in writing, software
119cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * distributed under the License is distributed on an "AS IS" BASIS,
129cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * See the License for the specific language governing permissions and
149cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * limitations under the License
159cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien */
169cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
17ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienpackage com.android.incallui;
18ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
19ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_CALL;
2069d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albertimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_AUDIO;
2169d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albertimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DIALPAD;
22d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DOWNGRADE_TO_AUDIO;
239cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_HOLD;
249cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MERGE;
2569d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albertimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MUTE;
2669d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albertimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
279cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
284d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
294d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienimport static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
304d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
314d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienimport android.content.Context;
324d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienimport android.os.Build;
33d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levienimport android.os.Bundle;
34ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienimport android.telecom.CallAudioState;
352a79f59e73294e43f32cc0138e23fcde34eec28aSeigo Nonakaimport android.telecom.InCallService.VideoCall;
36ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienimport android.telecom.VideoProfile;
37198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka
386d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonakaimport com.android.contacts.common.compat.CallSdkCompat;
394e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonakaimport com.android.contacts.common.compat.SdkVersionOverride;
4089e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonakaimport com.android.dialer.compat.UserManagerCompat;
41b80c1f19c58b927820a8a24bf2218e5645724608Raph Levienimport com.android.incallui.AudioModeProvider.AudioModeListener;
42bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levienimport com.android.incallui.InCallCameraManager.Listener;
439cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport com.android.incallui.InCallPresenter.CanAddCallListener;
449cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport com.android.incallui.InCallPresenter.InCallDetailsListener;
459cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport com.android.incallui.InCallPresenter.InCallState;
469cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport com.android.incallui.InCallPresenter.InCallStateListener;
479cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienimport com.android.incallui.InCallPresenter.IncomingCallListener;
488920e81717c6e51b92ff8f4479a1f959af260556John Reck
498920e81717c6e51b92ff8f4479a1f959af260556John Reck/**
508920e81717c6e51b92ff8f4479a1f959af260556John Reck * Logic for call buttons.
518920e81717c6e51b92ff8f4479a1f959af260556John Reck */
528920e81717c6e51b92ff8f4479a1f959af260556John Reckpublic class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButtonUi>
538920e81717c6e51b92ff8f4479a1f959af260556John Reck        implements InCallStateListener, AudioModeListener, IncomingCallListener,
548920e81717c6e51b92ff8f4479a1f959af260556John Reck        InCallDetailsListener, CanAddCallListener, Listener {
558920e81717c6e51b92ff8f4479a1f959af260556John Reck
568920e81717c6e51b92ff8f4479a1f959af260556John Reck    private static final String KEY_AUTOMATICALLY_MUTED = "incall_key_automatically_muted";
578920e81717c6e51b92ff8f4479a1f959af260556John Reck    private static final String KEY_PREVIOUS_MUTE_STATE = "incall_key_previous_mute_state";
588920e81717c6e51b92ff8f4479a1f959af260556John Reck
598920e81717c6e51b92ff8f4479a1f959af260556John Reck    private Call mCall;
608920e81717c6e51b92ff8f4479a1f959af260556John Reck    private boolean mAutomaticallyMuted = false;
618920e81717c6e51b92ff8f4479a1f959af260556John Reck    private boolean mPreviousMuteState = false;
628920e81717c6e51b92ff8f4479a1f959af260556John Reck
638920e81717c6e51b92ff8f4479a1f959af260556John Reck    public CallButtonPresenter() {
648920e81717c6e51b92ff8f4479a1f959af260556John Reck    }
658920e81717c6e51b92ff8f4479a1f959af260556John Reck
668920e81717c6e51b92ff8f4479a1f959af260556John Reck    @Override
678920e81717c6e51b92ff8f4479a1f959af260556John Reck    public void onUiReady(CallButtonUi ui) {
688920e81717c6e51b92ff8f4479a1f959af260556John Reck        super.onUiReady(ui);
698920e81717c6e51b92ff8f4479a1f959af260556John Reck
708920e81717c6e51b92ff8f4479a1f959af260556John Reck        AudioModeProvider.getInstance().addListener(this);
718920e81717c6e51b92ff8f4479a1f959af260556John Reck
728920e81717c6e51b92ff8f4479a1f959af260556John Reck        // register for call state changes last
738920e81717c6e51b92ff8f4479a1f959af260556John Reck        final InCallPresenter inCallPresenter = InCallPresenter.getInstance();
748920e81717c6e51b92ff8f4479a1f959af260556John Reck        inCallPresenter.addListener(this);
758920e81717c6e51b92ff8f4479a1f959af260556John Reck        inCallPresenter.addIncomingCallListener(this);
768920e81717c6e51b92ff8f4479a1f959af260556John Reck        inCallPresenter.addDetailsListener(this);
778920e81717c6e51b92ff8f4479a1f959af260556John Reck        inCallPresenter.addCanAddCallListener(this);
788920e81717c6e51b92ff8f4479a1f959af260556John Reck        inCallPresenter.getInCallCameraManager().addCameraSelectionListener(this);
798920e81717c6e51b92ff8f4479a1f959af260556John Reck
808920e81717c6e51b92ff8f4479a1f959af260556John Reck        // Update the buttons state immediately for the current call
818920e81717c6e51b92ff8f4479a1f959af260556John Reck        onStateChange(InCallState.NO_CALLS, inCallPresenter.getInCallState(),
828920e81717c6e51b92ff8f4479a1f959af260556John Reck                CallList.getInstance());
838920e81717c6e51b92ff8f4479a1f959af260556John Reck    }
848920e81717c6e51b92ff8f4479a1f959af260556John Reck
858920e81717c6e51b92ff8f4479a1f959af260556John Reck    @Override
868920e81717c6e51b92ff8f4479a1f959af260556John Reck    public void onUiUnready(CallButtonUi ui) {
878920e81717c6e51b92ff8f4479a1f959af260556John Reck        super.onUiUnready(ui);
888920e81717c6e51b92ff8f4479a1f959af260556John Reck
898920e81717c6e51b92ff8f4479a1f959af260556John Reck        InCallPresenter.getInstance().removeListener(this);
909cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        AudioModeProvider.getInstance().removeListener(this);
919cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        InCallPresenter.getInstance().removeIncomingCallListener(this);
9286fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien        InCallPresenter.getInstance().removeDetailsListener(this);
9386fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien        InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
94288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        InCallPresenter.getInstance().removeCanAddCallListener(this);
95288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
96288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
97288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    @Override
98288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
99288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        CallButtonUi ui = getUi();
100288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
10189e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka        if (newState == InCallState.OUTGOING) {
102aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien            mCall = callList.getOutgoingCall();
103288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        } else if (newState == InCallState.INCALL) {
104288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            mCall = callList.getActiveOrBackgroundCall();
105288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
106288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            // When connected to voice mail, automatically shows the dialpad.
107288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            // (On previous releases we showed it when in-call shows up, before waiting for
1084d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            // OUTGOING.  We may want to do that once we start showing "Voice mail" label on
1094d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            // the dialpad too.)
1104d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            if (ui != null) {
1114d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien                if (oldState == InCallState.OUTGOING && mCall != null) {
1124d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien                    if (CallerInfoUtils.isVoiceMailNumber(ui.getContext(), mCall)) {
1134d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien                        ui.displayDialpad(true /* show */, true /* animate */);
114cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe                    }
115cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe                }
116448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            }
1178e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        } else if (newState == InCallState.INCOMING) {
1189d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi            if (ui != null) {
1199d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi                ui.displayDialpad(false /* show */, true /* animate */);
1204d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            }
1214d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            mCall = callList.getIncomingCall();
1229d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi        } else {
1239d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi            mCall = null;
1249d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi        }
1259d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi        updateUi(newState, mCall);
1264d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
127288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
128288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    /**
129288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * Updates the user interface in response to a change in the details of a call.
130288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * Currently handles changes to the call buttons in response to a change in the details for a
131288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * call.  This is important to ensure changes to the active call are reflected in the available
132288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * buttons.
133288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     *
134288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * @param call The active call.
135288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     * @param details The call details.
136288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod     */
137288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    @Override
138288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void onDetailsChanged(Call call, android.telecom.Call.Details details) {
139288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        // Only update if the changes are for the currently active call
140288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        if (getUi() != null && call != null && call.equals(mCall)) {
141288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            updateButtonsState(call);
142288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        }
143288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
1444d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
145288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    @Override
146288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void onIncomingCall(InCallState oldState, InCallState newState, Call call) {
1474d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        onStateChange(oldState, newState, CallList.getInstance());
1484d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
1494d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1504d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    @Override
1514d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void onCanAddCallChanged(boolean canAddCall) {
152448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        if (getUi() != null && mCall != null) {
153448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            updateButtonsState(mCall);
1548e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        }
155893399732b64203133c22ad3c1f3535e46bf35a7Raph Levien    }
1566c4d167bff33c24c239d77ddb1044b18d180766aRaph Levien
1574d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    @Override
1584d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void onAudioMode(int mode) {
1594d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        if (getUi() != null) {
1609d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi            getUi().setAudio(mode);
1619d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi        }
1629d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi    }
1634d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1644d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    @Override
1654d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void onSupportedAudioMode(int mask) {
1664d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        if (getUi() != null) {
1674d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            getUi().setSupportedAudio(mask);
1684d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
1694d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
1704d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
171288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    @Override
172288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void onMute(boolean muted) {
173288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        if (getUi() != null && !mAutomaticallyMuted) {
174288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            getUi().setMute(muted);
175288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        }
176288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
177288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
178288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public int getAudioMode() {
179288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        return AudioModeProvider.getInstance().getAudioMode();
180288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
181288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
182288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public int getSupportedAudio() {
183288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        return AudioModeProvider.getInstance().getSupportedModes();
184288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
185288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
186288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void setAudioMode(int mode) {
1874d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1884d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // TODO: Set a intermediate state in this presenter until we get
189288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        // an update for onAudioMode().  This will make UI response immediate
1904d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // if it turns out to be slow
1914d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1924d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        Log.d(this, "Sending new Audio Mode: " + CallAudioState.audioRouteToString(mode));
1934d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        TelecomAdapter.getInstance().setAudioRoute(mode);
194288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
1954d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1964d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    /**
1974d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien     * Function assumes that bluetooth is not supported.
1984d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien     */
1994d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void toggleSpeakerphone() {
2004d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // this function should not be called if bluetooth is available
2014d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        if (0 != (CallAudioState.ROUTE_BLUETOOTH & getSupportedAudio())) {
2020bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader
2030bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            // It's clear the UI is wrong, so update the supported mode once again.
2040bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            Log.e(this, "toggling speakerphone not allowed when bluetooth supported.");
2050bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            getUi().setSupportedAudio(getSupportedAudio());
2060bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            return;
2074d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
2084d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
2094d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        int newMode = CallAudioState.ROUTE_SPEAKER;
2100bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader
2110bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        // if speakerphone is already on, change to wired/earpiece
2120bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        if (getAudioMode() == CallAudioState.ROUTE_SPEAKER) {
2130bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            newMode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
2144d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
2150bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader
2164d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        setAudioMode(newMode);
2174d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
2184d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
2190bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader    public void muteClicked(boolean checked) {
2204d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        Log.d(this, "turning on mute: " + checked);
2214d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        TelecomAdapter.getInstance().mute(checked);
2224d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
2234d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
2244d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void holdClicked(boolean checked) {
2254d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        if (mCall == null) {
226448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            return;
227448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        }
228448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        if (checked) {
229448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            Log.i(this, "Putting the call on hold: " + mCall);
230448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            TelecomAdapter.getInstance().holdCall(mCall.getId());
231448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        } else {
232448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            Log.i(this, "Removing the call from hold: " + mCall);
2338e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            TelecomAdapter.getInstance().unholdCall(mCall.getId());
234448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        }
2356c4d167bff33c24c239d77ddb1044b18d180766aRaph Levien    }
236448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien
237288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    public void swapClicked() {
238288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        if (mCall == null) {
2394d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            return;
2404d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
2419d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi
2424d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        Log.i(this, "Swapping the call: " + mCall);
2434d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        TelecomAdapter.getInstance().swap(mCall.getId());
2444d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
2454d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
2464d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void mergeClicked() {
247448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        TelecomAdapter.getInstance().merge(mCall.getId());
248448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien    }
2498e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod
250448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien    public void addCallClicked() {
2516c4d167bff33c24c239d77ddb1044b18d180766aRaph Levien        // Automatically mute the current call
2524d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        mAutomaticallyMuted = true;
253288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        mPreviousMuteState = AudioModeProvider.getInstance().getMute();
2544d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // Simulate a click on the mute button
2554d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        muteClicked(true);
2564d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        TelecomAdapter.getInstance().addCall();
2574d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
2584d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
2594d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    public void changeToVoiceClicked() {
2604d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        VideoCall videoCall = mCall.getVideoCall();
261ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (videoCall == null) {
262ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            return;
263ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
264ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
265ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_AUDIO_ONLY);
266ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        videoCall.sendSessionModifyRequest(videoProfile);
267ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
268ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
269ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    public void showDialpadClicked(boolean checked) {
270ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        Log.v(this, "Show dialpad " + String.valueOf(checked));
271ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        getUi().displayDialpad(checked /* show */, true /* animate */);
2726da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    }
2739cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
2749cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    public void changeToVideoClicked() {
2759cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        VideoCall videoCall = mCall.getVideoCall();
2766da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        if (videoCall == null) {
2776da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod            return;
2786da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        }
2796da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        int currVideoState = mCall.getVideoState();
2806da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(currVideoState);
2816da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
2826da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod
2836da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod        VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
2844d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        videoCall.sendSessionModifyRequest(videoProfile);
2859cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
2869cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
2879cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
288bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka    /**
289bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka     * Switches the camera between the front-facing and back-facing camera.
2904d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien     * @param useFrontFacingCamera True if we should switch to using the front-facing camera, or
291bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien     *     false if we should switch to using the back-facing camera.
292bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien     */
293bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien    public void switchCameraClicked(boolean useFrontFacingCamera) {
2949cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        InCallCameraManager cameraManager = InCallPresenter.getInstance().getInCallCameraManager();
2959cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        cameraManager.setUseFrontFacingCamera(useFrontFacingCamera);
296bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka
297bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        VideoCall videoCall = mCall.getVideoCall();
298bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        if (videoCall == null) {
2999cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            return;
3009cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3019cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
3029cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        String cameraId = cameraManager.getActiveCameraId();
3039cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        if (cameraId != null) {
3049cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            final int cameraDir = cameraManager.isUsingFrontFacingCamera()
3059cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien                    ? Call.VideoSettings.CAMERA_DIRECTION_FRONT_FACING
3069cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien                    : Call.VideoSettings.CAMERA_DIRECTION_BACK_FACING;
3079cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            mCall.getVideoSettings().setCameraDir(cameraDir);
3089cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.setCamera(cameraId);
3099cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.requestCameraCapabilities();
3109cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3119cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
3129cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
3139cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
3149cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    /**
3159cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien     * Stop or start client's video transmission.
3169cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien     * @param pause True if pausing the local user's video, or false if starting the local user's
3179cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien     *    video.
3189cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien     */
3199cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    public void pauseVideoClicked(boolean pause) {
3209cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        VideoCall videoCall = mCall.getVideoCall();
3219cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        if (videoCall == null) {
3229cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            return;
3239cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3249cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
3259cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        final int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(mCall.getVideoState());
3269cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        if (pause) {
3279cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.setCamera(null);
3289cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState
3299cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien                    & ~VideoProfile.STATE_TX_ENABLED);
3309cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.sendSessionModifyRequest(videoProfile);
3319cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        } else {
3329cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            InCallCameraManager cameraManager = InCallPresenter.getInstance().
3339a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien                    getInCallCameraManager();
3349cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.setCamera(cameraManager.getActiveCameraId());
3359cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState
3369a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien                    | VideoProfile.STATE_TX_ENABLED);
3379cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            videoCall.sendSessionModifyRequest(videoProfile);
3389cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
3399cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3409cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        getUi().setVideoPaused(pause);
3414d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
3424d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
3434d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    private void updateUi(InCallState state, Call call) {
34489e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka        Log.d(this, "Updating call UI for call: ", call);
34589e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka
3464d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        final CallButtonUi ui = getUi();
3474d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        if (ui == null) {
3489cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            return;
3499cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3509cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
351ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean isEnabled =
3524d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien                state.isConnectingOrConnected() &&!state.isIncoming() && call != null;
353ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.setEnabled(isEnabled);
3540bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader
355ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (call == null) {
356ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            return;
357ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
358ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
3594d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        updateButtonsState(call);
360ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
361ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
362ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    /**
363ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien     * Updates the buttons applicable for the UI.
364ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien     *
365ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien     * @param call The active call.
366ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien     */
367ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    private void updateButtonsState(Call call) {
368ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        Log.v(this, "updateButtonsState");
369ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final CallButtonUi ui = getUi();
370ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean isVideo = VideoUtils.isVideoCall(call);
371ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
372b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod        // Common functionality (audio, hold, etc).
373b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod        // Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
374ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        //     (1) If the device normally can hold, show HOLD in a disabled state.
375b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod        //     (2) If the device doesn't have the concept of hold/swap, remove the button.
376ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showSwap = call.can(
377ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                android.telecom.Call.Details.CAPABILITY_SWAP_CONFERENCE);
378ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showHold = !showSwap
379ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORT_HOLD)
380ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                && call.can(android.telecom.Call.Details.CAPABILITY_HOLD);
381ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean isCallOnHold = call.getState() == Call.State.ONHOLD;
3824d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
383ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showAddCall = TelecomAdapter.getInstance().canAddCall()
384ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                && UserManagerCompat.isUserUnlocked(ui.getContext());
385ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showMerge = call.can(
386ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
387ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showUpgradeToVideo = !isVideo && hasVideoCallCapabilities(call);
388ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showDowngradeToAudio = isVideo && isDowngradeToAudioSupported(call);
389ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        final boolean showMute = call.can(android.telecom.Call.Details.CAPABILITY_MUTE);
390ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
391ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_AUDIO, true);
392ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_SWAP, showSwap);
393ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_HOLD, showHold);
394ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.setHold(isCallOnHold);
395ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_MUTE, showMute);
396ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_ADD_CALL, showAddCall);
397ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
398ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_DOWNGRADE_TO_AUDIO, showDowngradeToAudio);
399ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
400ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
401ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (isVideo) {
402ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            getUi().setVideoPaused(!VideoUtils.isTransmissionEnabled(call));
403ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
404ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_DIALPAD, true);
405ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.showButton(BUTTON_MERGE, showMerge);
406ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
407ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        ui.updateButtonStates();
408ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
409ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
410ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    private boolean hasVideoCallCapabilities(Call call) {
411ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (SdkVersionOverride.getSdkVersion(Build.VERSION_CODES.M) >= Build.VERSION_CODES.M) {
412ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
413ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                    && call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX);
4144d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
415e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien        // In L, this single flag represents both video transmitting and receiving capabilities
416e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien        return call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX);
417e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    }
418e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien
419e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    /**
420e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     * Determines if downgrading from a video call to an audio-only call is supported.  In order to
421e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     * support downgrade to audio, the SDK version must be >= N and the call should NOT have the
422e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     * {@link android.telecom.Call.Details#CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO}.
423e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     * @param call The call.
424e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     * @return {@code true} if downgrading to an audio-only call from a video call is supported.
425e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien     */
426e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    private boolean isDowngradeToAudioSupported(Call call) {
427e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien        return !call.can(CallSdkCompat.Details.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO);
428e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    }
429e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien
430e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    public void refreshMuteState() {
431e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien        // Restore the previous mute state
432e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien        if (mAutomaticallyMuted &&
433e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                AudioModeProvider.getInstance().getMute() != mPreviousMuteState) {
434e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            if (getUi() == null) {
435e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                return;
436e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            }
4376292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            muteClicked(mPreviousMuteState);
4386292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4396292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mAutomaticallyMuted = false;
4406292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4416292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4426292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    @Override
4436292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    public void onSaveInstanceState(Bundle outState) {
4446292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        super.onSaveInstanceState(outState);
4456292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        outState.putBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
4466292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        outState.putBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
4476292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4486292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4496292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    @Override
4506292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    public void onRestoreInstanceState(Bundle savedInstanceState) {
4516292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mAutomaticallyMuted =
4526292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi                savedInstanceState.getBoolean(KEY_AUTOMATICALLY_MUTED, mAutomaticallyMuted);
4536292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mPreviousMuteState =
4546292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi                savedInstanceState.getBoolean(KEY_PREVIOUS_MUTE_STATE, mPreviousMuteState);
4556292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        super.onRestoreInstanceState(savedInstanceState);
4566292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4576292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4586292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    public interface CallButtonUi extends Ui {
4596292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void showButton(int buttonId, boolean show);
4606292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void enableButton(int buttonId, boolean enable);
4616292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setEnabled(boolean on);
4626292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setMute(boolean on);
4636292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setHold(boolean on);
4646292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setCameraSwitched(boolean isBackFacingCamera);
4656292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setVideoPaused(boolean isPaused);
4666292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setAudio(int mode);
4676292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void setSupportedAudio(int mask);
4686292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void displayDialpad(boolean on, boolean animate);
4696292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        boolean isDialpadVisible();
4706292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4716292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        /**
4726292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi         * Once showButton() has been called on each of the individual buttons in the UI, call
4736292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi         * this to configure the overflow menu appropriately.
4746292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi         */
4756292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void updateButtonStates();
4766292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        Context getContext();
4776292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4786292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4796292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    @Override
4806292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    public void onActiveCameraSelectionChanged(boolean isUsingFrontFacingCamera) {
4816292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        if (getUi() == null) {
4826292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            return;
4836292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4846292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        getUi().setCameraSwitched(!isUsingFrontFacingCamera);
4856292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4866292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi}
4876292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi