diff options
Diffstat (limited to 'InCallUI/src/com/android')
13 files changed, 314 insertions, 142 deletions
diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java index 07cbfd573..d68a316c9 100644 --- a/InCallUI/src/com/android/incallui/AnswerFragment.java +++ b/InCallUI/src/com/android/incallui/AnswerFragment.java @@ -21,6 +21,7 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; +import android.telecom.VideoProfile; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; @@ -49,10 +50,7 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente public static final int TARGET_SET_FOR_AUDIO_WITH_SMS = 1; public static final int TARGET_SET_FOR_VIDEO_WITHOUT_SMS = 2; public static final int TARGET_SET_FOR_VIDEO_WITH_SMS = 3; - public static final int TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST = 4; - public static final int TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST = 5; - public static final int TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST = 6; - public static final int TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST = 7; + public static final int TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST = 4; /** * The popup showing the list of canned responses. @@ -124,12 +122,21 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente * Sets targets on the glowpad according to target set identified by the parameter. * @param targetSet Integer identifying the set of targets to use. */ - @Override public void showTargets(int targetSet) { + showTargets(targetSet, VideoProfile.VideoState.BIDIRECTIONAL); + } + + /** + * Sets targets on the glowpad according to target set identified by the parameter. + * @param targetSet Integer identifying the set of targets to use. + */ + @Override + public void showTargets(int targetSet, int videoState) { final int targetResourceId; final int targetDescriptionsResourceId; final int directionDescriptionsResourceId; final int handleDrawableResourceId; + mGlowpad.setVideoState(videoState); switch (targetSet) { case TARGET_SET_FOR_AUDIO_WITH_SMS: @@ -156,39 +163,13 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente R.array.incoming_call_widget_video_with_sms_direction_descriptions; handleDrawableResourceId = R.drawable.ic_incall_video_handle; break; - case TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST: - targetResourceId = R.array.incoming_call_widget_video_upgrade_request_targets; - targetDescriptionsResourceId = - R.array.incoming_call_widget_video_upgrade_request_target_descriptions; - directionDescriptionsResourceId = R.array - .incoming_call_widget_video_upgrade_request_target_direction_descriptions; - handleDrawableResourceId = R.drawable.ic_incall_video_handle; - break; - case TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST: + case TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST: targetResourceId = - R.array.incoming_call_widget_bidirectional_video_accept_reject_request_targets; + R.array.incoming_call_widget_video_request_targets; targetDescriptionsResourceId = - R.array.incoming_call_widget_video_upgrade_request_target_descriptions; + R.array.incoming_call_widget_video_request_target_descriptions; directionDescriptionsResourceId = R.array - .incoming_call_widget_video_upgrade_request_target_direction_descriptions; - handleDrawableResourceId = R.drawable.ic_incall_video_handle; - break; - case TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST: - targetResourceId = - R.array.incoming_call_widget_video_transmit_accept_reject_request_targets; - targetDescriptionsResourceId = - R.array.incoming_call_widget_video_transmit_request_target_descriptions; - directionDescriptionsResourceId = R.array - .incoming_call_widget_video_upgrade_request_target_direction_descriptions; - handleDrawableResourceId = R.drawable.ic_incall_video_handle; - break; - case TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST: - targetResourceId = - R.array.incoming_call_widget_video_receive_accept_reject_request_targets; - targetDescriptionsResourceId = - R.array.incoming_call_widget_video_receive_request_target_descriptions; - directionDescriptionsResourceId = R.array - .incoming_call_widget_video_upgrade_request_target_direction_descriptions; + .incoming_call_widget_video_request_target_direction_descriptions; handleDrawableResourceId = R.drawable.ic_incall_video_handle; break; case TARGET_SET_FOR_AUDIO_WITHOUT_SMS: @@ -376,6 +357,11 @@ public class AnswerFragment extends BaseFragment<AnswerPresenter, AnswerPresente } @Override + public void onDeclineUpgradeRequest(Context context) { + InCallPresenter.getInstance().declineUpgradeRequest(context); + } + + @Override public void onText() { getPresenter().onText(); } diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java index dc2d9c551..6628f2f86 100644 --- a/InCallUI/src/com/android/incallui/AnswerPresenter.java +++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java @@ -97,6 +97,17 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> public void onDisconnect(Call call) { // no-op } + + public void onSessionModificationStateChange(int sessionModificationState) { + boolean isUpgradePending = sessionModificationState == + Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; + + if (!isUpgradePending) { + // Stop listening for updates. + CallList.getInstance().removeCallUpdateListener(mCallId, this); + showAnswerUi(false); + } + } private boolean isVideoUpgradePending(Call call) { return call.getSessionModificationState() @@ -166,27 +177,15 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> return; } - showAnswerUi(true); - getUi().showTargets(getUiTarget(currentVideoState, modifyToVideoState)); - - } + AnswerUi ui = getUi(); - private int getUiTarget(int currentVideoState, int modifyToVideoState) { - if (showVideoUpgradeOptions(currentVideoState, modifyToVideoState)) { - return AnswerFragment.TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST; - } else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.BIDIRECTIONAL)) { - return AnswerFragment.TARGET_SET_FOR_BIDIRECTIONAL_VIDEO_ACCEPT_REJECT_REQUEST; - } else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.TX_ENABLED)) { - return AnswerFragment.TARGET_SET_FOR_VIDEO_TRANSMIT_ACCEPT_REJECT_REQUEST; - } else if (isEnabled(modifyToVideoState, VideoProfile.VideoState.RX_ENABLED)) { - return AnswerFragment.TARGET_SET_FOR_VIDEO_RECEIVE_ACCEPT_REJECT_REQUEST; + if (ui == null) { + Log.e(this, "Ui is null. Can't process upgrade request"); + return; } - return AnswerFragment.TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST; - } - - private boolean showVideoUpgradeOptions(int currentVideoState, int modifyToVideoState) { - return currentVideoState == VideoProfile.VideoState.AUDIO_ONLY && - isEnabled(modifyToVideoState, VideoProfile.VideoState.BIDIRECTIONAL); + showAnswerUi(true); + ui.showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_ACCEPT_REJECT_REQUEST, + modifyToVideoState); } private boolean isEnabled(int videoState, int mask) { @@ -220,15 +219,16 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> } public void onAnswer(int videoState, Context context) { - Log.d(this, "onAnswer mCallId=" + mCallId + " videoState=" + videoState); if (mCallId == null) { return; } if (mCall.getSessionModificationState() == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + Log.d(this, "onAnswer (upgradeCall) mCallId=" + mCallId + " videoState=" + videoState); InCallPresenter.getInstance().acceptUpgradeRequest(videoState, context); } else { + Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState); TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState); } } @@ -293,6 +293,7 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi> interface AnswerUi extends Ui { public void onShowAnswerUi(boolean shown); public void showTargets(int targetSet); + public void showTargets(int targetSet, int videoState); public void showMessageDialog(); public void configureMessageDialog(List<String> textResponses); public Context getContext(); diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java index 18eb7471e..9f58fbed5 100644 --- a/InCallUI/src/com/android/incallui/Call.java +++ b/InCallUI/src/com/android/incallui/Call.java @@ -124,6 +124,7 @@ public class Call { public static final int REQUEST_FAILED = 2; public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3; public static final int UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT = 4; + public static final int REQUEST_REJECTED = 5; } public static class VideoSettings { @@ -510,7 +511,7 @@ public class Call { Log.d(this, "setSessionModificationState " + state + " mSessionModificationState=" + mSessionModificationState); if (hasChanged) { - update(); + CallList.getInstance().onSessionModificationStateChange(this, state); } } diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java index 506ca7dcc..9def35694 100644 --- a/InCallUI/src/com/android/incallui/CallButtonFragment.java +++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java @@ -18,9 +18,7 @@ package com.android.incallui; import static com.android.incallui.CallButtonFragment.Buttons.*; -import android.app.AlertDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.res.ColorStateList; import android.content.res.Resources; import android.graphics.drawable.Drawable; @@ -30,8 +28,6 @@ import android.graphics.drawable.RippleDrawable; import android.graphics.drawable.StateListDrawable; import android.os.Bundle; import android.telecom.AudioState; -import android.telecom.TelecomManager; -import android.telecom.VideoProfile; import android.util.SparseIntArray; import android.view.ContextThemeWrapper; import android.view.HapticFeedbackConstants; @@ -43,12 +39,10 @@ import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.ImageButton; import android.widget.PopupMenu; -import android.widget.Toast; import android.widget.PopupMenu.OnDismissListener; import android.widget.PopupMenu.OnMenuItemClickListener; import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; -import java.util.ArrayList; /** * Fragment for call control buttons @@ -58,7 +52,7 @@ public class CallButtonFragment implements CallButtonPresenter.CallButtonUi, OnMenuItemClickListener, OnDismissListener, View.OnClickListener { private static final int INVALID_INDEX = -1; - private static final int BUTTON_MAX_VISIBLE = 5; + private int mButtonMaxVisible; // The button is currently visible in the UI private static final int BUTTON_VISIBLE = 1; // The button is hidden in the UI @@ -127,6 +121,8 @@ public class CallButtonFragment for (int i = 0; i < BUTTON_COUNT; i++) { mButtonVisibilityMap.put(i, BUTTON_HIDDEN); } + + mButtonMaxVisible = getResources().getInteger(R.integer.call_card_max_buttons); } @Override @@ -458,7 +454,7 @@ public class CallButtonFragment final View button = getButtonById(i); if (visibility == BUTTON_VISIBLE) { visibleCount++; - if (visibleCount <= BUTTON_MAX_VISIBLE) { + if (visibleCount <= mButtonMaxVisible) { button.setVisibility(View.VISIBLE); prevVisibleButton = button; prevVisibleId = i; diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java index 43ee3326b..d788a1097 100644 --- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java +++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java @@ -322,6 +322,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto VideoProfile videoProfile = new VideoProfile( mCall.getVideoState() | VideoProfile.VideoState.TX_ENABLED); videoCall.sendSessionModifyRequest(videoProfile); + mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE); } getUi().setVideoPaused(pause); } diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java index 691589ca0..2acabd694 100644 --- a/InCallUI/src/com/android/incallui/CallCardFragment.java +++ b/InCallUI/src/com/android/incallui/CallCardFragment.java @@ -31,6 +31,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.os.Trace; +import android.os.Handler; +import android.os.Looper; import android.telecom.DisconnectCause; import android.telecom.VideoProfile; import android.telephony.PhoneNumberUtils; @@ -63,6 +65,38 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr implements CallCardPresenter.CallCardUi { private static final String TAG = "CallCardFragment"; + /** + * Internal class which represents the call state label which is to be applied. + */ + private class CallStateLabel { + private CharSequence mCallStateLabel; + private boolean mIsAutoDismissing; + + public CallStateLabel(CharSequence callStateLabel, boolean isAutoDismissing) { + mCallStateLabel = callStateLabel; + mIsAutoDismissing = isAutoDismissing; + } + + public CharSequence getCallStateLabel() { + return mCallStateLabel; + } + + /** + * Determines if the call state label should auto-dismiss. + * + * @return {@code true} if the call state label should auto-dismiss. + */ + public boolean isAutoDismissing() { + return mIsAutoDismissing; + } + }; + + /** + * The duration of time (in milliseconds) a call state label should remain visible before + * resetting to its previous value. + */ + private static final long CALL_STATE_LABEL_RESET_DELAY_MS = 3000; + private AnimatorSet mAnimatorSet; private int mShrinkAnimationDuration; private int mFabNormalDiameter; @@ -119,6 +153,13 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr private MaterialPalette mCurrentThemeColors; + /** + * Call state label to set when an auto-dismissing call state label is dismissed. + */ + private CharSequence mPostResetCallStateLabel; + private boolean mCallStateLabelResetPending = false; + private Handler mHandler; + @Override public CallCardPresenter.CallCardUi getUi() { return this; @@ -133,6 +174,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mHandler = new Handler(Looper.getMainLooper()); mShrinkAnimationDuration = getResources().getInteger(R.integer.shrink_animation_duration); mVideoAnimationDuration = getResources().getInteger(R.integer.video_animation_duration); mFloatingActionButtonVerticalOffset = getResources().getDimensionPixelOffset( @@ -492,15 +534,16 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr boolean isWifi, boolean isConference) { boolean isGatewayCall = !TextUtils.isEmpty(gatewayNumber); - CharSequence callStateLabel = getCallStateLabelFromState(state, videoState, + CallStateLabel callStateLabel = getCallStateLabelFromState(state, videoState, sessionModificationState, disconnectCause, connectionLabel, isGatewayCall, isWifi, isConference); - Log.v(this, "setCallState " + callStateLabel); + Log.v(this, "setCallState " + callStateLabel.getCallStateLabel()); + Log.v(this, "AutoDismiss " + callStateLabel.isAutoDismissing()); Log.v(this, "DisconnectCause " + disconnectCause.toString()); Log.v(this, "gateway " + connectionLabel + gatewayNumber); - if (TextUtils.equals(callStateLabel, mCallStateLabel.getText())) { + if (TextUtils.equals(callStateLabel.getCallStateLabel(), mCallStateLabel.getText())) { // Nothing to do if the labels are the same if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) { mCallStateLabel.clearAnimation(); @@ -510,24 +553,13 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr } // Update the call state label and icon. - if (!TextUtils.isEmpty(callStateLabel)) { - mCallStateLabel.setText(callStateLabel); - mCallStateLabel.setAlpha(1); - mCallStateLabel.setVisibility(View.VISIBLE); - + setCallStateLabel(callStateLabel); + if (!TextUtils.isEmpty(callStateLabel.getCallStateLabel())) { if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED) { mCallStateLabel.clearAnimation(); } else { mCallStateLabel.startAnimation(mPulseAnimation); } - } else { - Animation callStateLabelAnimation = mCallStateLabel.getAnimation(); - if (callStateLabelAnimation != null) { - callStateLabelAnimation.cancel(); - } - mCallStateLabel.setText(null); - mCallStateLabel.setAlpha(0); - mCallStateLabel.setVisibility(View.GONE); } if (callStateIcon != null) { @@ -538,7 +570,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr mCallStateIcon.setImageDrawable(callStateIcon); if (state == Call.State.ACTIVE || state == Call.State.CONFERENCED - || TextUtils.isEmpty(callStateLabel)) { + || TextUtils.isEmpty(callStateLabel.getCallStateLabel())) { mCallStateIcon.clearAnimation(); } else { mCallStateIcon.startAnimation(mPulseAnimation); @@ -569,7 +601,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr if (state == Call.State.INCOMING) { if (callStateLabel != null) { - getView().announceForAccessibility(callStateLabel); + getView().announceForAccessibility(callStateLabel.getCallStateLabel()); } if (mPrimaryName.getText() != null) { getView().announceForAccessibility(mPrimaryName.getText()); @@ -577,6 +609,50 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr } } + private void setCallStateLabel(CallStateLabel callStateLabel) { + Log.v(this, "setCallStateLabel : label = " + callStateLabel.getCallStateLabel()); + + if (callStateLabel.isAutoDismissing()) { + mCallStateLabelResetPending = true; + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + Log.v(this, "restoringCallStateLabel : label = " + + mPostResetCallStateLabel); + changeCallStateLabel(mPostResetCallStateLabel); + mCallStateLabelResetPending = false; + } + }, CALL_STATE_LABEL_RESET_DELAY_MS); + + changeCallStateLabel(callStateLabel.getCallStateLabel()); + } else { + // Keep track of the current call state label; used when resetting auto dismissing + // call state labels. + mPostResetCallStateLabel = callStateLabel.getCallStateLabel(); + + if (!mCallStateLabelResetPending) { + changeCallStateLabel(callStateLabel.getCallStateLabel()); + } + } + } + + private void changeCallStateLabel(CharSequence callStateLabel) { + Log.v(this, "changeCallStateLabel : label = " + callStateLabel); + if (!TextUtils.isEmpty(callStateLabel)) { + mCallStateLabel.setText(callStateLabel); + mCallStateLabel.setAlpha(1); + mCallStateLabel.setVisibility(View.VISIBLE); + } else { + Animation callStateLabelAnimation = mCallStateLabel.getAnimation(); + if (callStateLabelAnimation != null) { + callStateLabelAnimation.cancel(); + } + mCallStateLabel.setText(null); + mCallStateLabel.setAlpha(0); + mCallStateLabel.setVisibility(View.GONE); + } + } + @Override public void setCallbackNumber(String callbackNumber, boolean isEmergencyCall) { if (mInCallMessageLabel == null) { @@ -670,7 +746,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr * * TODO: Move this to the CallCardPresenter. */ - private CharSequence getCallStateLabelFromState(int state, int videoState, + private CallStateLabel getCallStateLabelFromState(int state, int videoState, int sessionModificationState, DisconnectCause disconnectCause, String label, boolean isGatewayCall, boolean isWifi, boolean isConference) { final Context context = getView().getContext(); @@ -678,6 +754,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr boolean hasSuggestedLabel = label != null; boolean isAccount = hasSuggestedLabel && !isGatewayCall; + boolean isAutoDismissing = false; switch (state) { case Call.State.IDLE: @@ -689,15 +766,20 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr if ((isAccount || isWifi || isConference) && hasSuggestedLabel) { callStateLabel = label; } else if (sessionModificationState + == Call.SessionModificationState.REQUEST_REJECTED) { + callStateLabel = context.getString(R.string.card_title_video_call_rejected); + isAutoDismissing = true; + } else if (sessionModificationState == Call.SessionModificationState.REQUEST_FAILED) { callStateLabel = context.getString(R.string.card_title_video_call_error); + isAutoDismissing = true; } else if (sessionModificationState == Call.SessionModificationState.WAITING_FOR_RESPONSE) { callStateLabel = context.getString(R.string.card_title_video_call_requesting); - } else if (CallUtils.isVideoCall(videoState) && - VideoProfile.VideoState.isPaused(videoState)) { - callStateLabel = context.getString(R.string.card_title_video_call_paused); - } else if (VideoProfile.VideoState.isBidirectional(videoState)) { + } else if (sessionModificationState + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + callStateLabel = context.getString(R.string.card_title_video_call_requesting); + } else if (CallUtils.isVideoCall(videoState)) { callStateLabel = context.getString(R.string.card_title_video_call); } break; @@ -721,7 +803,8 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr callStateLabel = label; } else if (isAccount) { callStateLabel = context.getString(R.string.incoming_via_template, label); - } else if (VideoProfile.VideoState.isBidirectional(videoState)) { + } else if (VideoProfile.VideoState.isTransmissionEnabled(videoState) || + VideoProfile.VideoState.isReceptionEnabled(videoState)) { callStateLabel = context.getString(R.string.notification_incoming_video_call); } else { callStateLabel = context.getString(R.string.card_title_incoming_call); @@ -748,7 +831,7 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr default: Log.wtf(this, "updateCallStateWidgets: unexpected call: " + state); } - return callStateLabel; + return new CallStateLabel(callStateLabel, isAutoDismissing); } private void showAndInitializeSecondaryCallInfo(boolean hasProvider) { diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index ee13e061f..b233ad56b 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -48,6 +48,7 @@ import com.android.incallui.InCallPresenter.IncomingCallListener; import com.android.incalluibind.ObjectFactory; import java.lang.ref.WeakReference; +import java.util.Objects; import com.google.common.base.Preconditions; @@ -58,7 +59,7 @@ import com.google.common.base.Preconditions; */ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> implements InCallStateListener, IncomingCallListener, InCallDetailsListener, - InCallEventListener { + InCallEventListener, CallList.CallUpdateListener { public interface EmergencyCallListener { public void onCallUpdated(BaseFragment fragment, boolean isEmergency); @@ -75,8 +76,8 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> private ContactCacheEntry mPrimaryContactInfo; private ContactCacheEntry mSecondaryContactInfo; private CallTimer mCallTimer; - private Context mContext; + private boolean mSpinnerShowing = false; public static class ContactLookupCallback implements ContactInfoCacheCallback { private final WeakReference<CallCardPresenter> mCallCardPresenter; @@ -121,6 +122,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> // Call may be null if disconnect happened already. if (call != null) { mPrimary = call; + CallList.getInstance().addCallUpdateListener(call.getId(), this); // start processing lookups right away. if (!call.isConferenceCall()) { @@ -158,6 +160,9 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> InCallPresenter.getInstance().removeIncomingCallListener(this); InCallPresenter.getInstance().removeDetailsListener(this); InCallPresenter.getInstance().removeInCallEventListener(this); + if (mPrimary != null) { + CallList.getInstance().removeCallUpdateListener(mPrimary.getId(), this); + } mPrimary = null; mPrimaryContactInfo = null; @@ -204,6 +209,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> final boolean secondaryChanged = !Call.areSame(mSecondary, secondary); mSecondary = secondary; + Call previousPrimary = mPrimary; mPrimary = primary; // Refresh primary call information if either: @@ -212,6 +218,11 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> if (mPrimary != null && (primaryChanged || ui.isManageConferenceVisible() != shouldShowManageConference())) { // primary call has changed + if (previousPrimary != null) { + CallList.getInstance().removeCallUpdateListener(previousPrimary.getId(), this); + } + CallList.getInstance().addCallUpdateListener(mPrimary.getId(), this); + mPrimaryContactInfo = ContactInfoCache.buildCacheEntryFromCall(mContext, mPrimary, mPrimary.getState() == Call.State.INCOMING); updatePrimaryDisplayInfo(); @@ -262,7 +273,6 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } maybeShowManageConferenceCallButton(); - maybeShowProgressSpinner(); // Hide the end call button instantly if we're receiving an incoming call. getUi().setEndCallButtonEnabled(shouldShowEndCallButton(mPrimary, callState), @@ -279,6 +289,32 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } } + @Override + public void onCallChanged(Call call) { + // No-op; specific call updates handled elsewhere. + } + + /** + * Handles a change to the session modification state for a call. Triggers showing the progress + * spinner, as well as updating the call state label. + * + * @param sessionModificationState The new session modification state. + */ + @Override + public void onSessionModificationStateChange(int sessionModificationState) { + Log.d(this, "onSessionModificationStateChange : sessionModificationState = " + + sessionModificationState); + + if (mPrimary == null) { + return; + } + maybeShowProgressSpinner(mPrimary.getState(), sessionModificationState); + getUi().setEndCallButtonEnabled(sessionModificationState != + Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST, + true /* shouldAnimate */); + updatePrimaryCallState(); + } + private String getSubscriptionNumber() { // If it's an emergency call, and they're not populating the callback number, // then try to fall back to the phone sub info (to hopefully get the SIM's @@ -322,11 +358,21 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> getUi().showManageConferenceCallButton(shouldShowManageConference()); } - private void maybeShowProgressSpinner() { - final boolean show = mPrimary != null && mPrimary.getSessionModificationState() - == Call.SessionModificationState.WAITING_FOR_RESPONSE - && mPrimary.getState() == Call.State.ACTIVE; - getUi().setProgressSpinnerVisible(show); + /** + * Determines if a pending session modification exists for the current call. If so, the + * progress spinner is shown, and the call state is updated. + * + * @param callState The call state. + * @param sessionModificationState The session modification state. + */ + private void maybeShowProgressSpinner(int callState, int sessionModificationState) { + final boolean show = sessionModificationState == + Call.SessionModificationState.WAITING_FOR_RESPONSE + && callState == Call.State.ACTIVE; + if (show != mSpinnerShowing) { + getUi().setProgressSpinnerVisible(show); + mSpinnerShowing = show; + } } /** @@ -657,7 +703,7 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> } private boolean hasOutgoingGatewayCall() { - // We only display the gateway information while STATE_DIALING so return false for any othe + // We only display the gateway information while STATE_DIALING so return false for any other // call state. // TODO: mPrimary can be null because this is called from updatePrimaryDisplayInfo which // is also called after a contact search completes (call is not present yet). Split the diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index d89fead9a..e937bb162 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -142,6 +142,21 @@ public class CallList { Trace.endSection(); } + /** + * Called when a single call has changed session modification state. + * + * @param call The call. + * @param sessionModificationState The new session modification state. + */ + public void onSessionModificationStateChange(Call call, int sessionModificationState) { + final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId()); + if (listeners != null) { + for (CallUpdateListener listener : listeners) { + listener.onSessionModificationStateChange(sessionModificationState); + } + } + } + public void notifyCallUpdateListeners(Call call) { final List<CallUpdateListener> listeners = mCallUpdateListenerMap.get(call.getId()); if (listeners != null) { @@ -556,10 +571,19 @@ public class CallList { * that will get called upon disconnection. */ public void onDisconnect(Call call); + + } public interface CallUpdateListener { // TODO: refactor and limit arg to be call state. Caller info is not needed. public void onCallChanged(Call call); + + /** + * Notifies of a change to the session modification state for a call. + * + * @param sessionModificationState The new session modification state. + */ + public void onSessionModificationStateChange(int sessionModificationState); } } diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java index 58a5f30ea..177669668 100644 --- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java +++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java @@ -49,6 +49,7 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger private AnswerListener mAnswerListener; private boolean mPingEnabled = true; private boolean mTargetTriggered = false; + private int mVideoState = VideoProfile.VideoState.BIDIRECTIONAL; public GlowPadWrapper(Context context) { super(context); @@ -125,11 +126,11 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger break; case R.drawable.ic_videocam: case R.drawable.ic_lockscreen_answer_video: - mAnswerListener.onAnswer(VideoProfile.VideoState.BIDIRECTIONAL, getContext()); + mAnswerListener.onAnswer(mVideoState, getContext()); mTargetTriggered = true; break; - case R.drawable.ic_toolbar_video_off: - InCallPresenter.getInstance().declineUpgradeRequest(getContext()); + case R.drawable.ic_lockscreen_decline_video: + mAnswerListener.onDeclineUpgradeRequest(getContext()); mTargetTriggered = true; break; default: @@ -152,9 +153,19 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger mAnswerListener = listener; } + /** + * Sets the video state represented by the "video" icon on the glow pad. + * + * @param videoState The new video state. + */ + public void setVideoState(int videoState) { + mVideoState = videoState; + } + public interface AnswerListener { void onAnswer(int videoState, Context context); void onDecline(Context context); + void onDeclineUpgradeRequest(Context context); void onText(); } } diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java index ba4ab660d..ede1129cd 100644 --- a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java +++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java @@ -80,6 +80,19 @@ public class InCallVideoCallCallback extends VideoCall.Callback { Log.d(this, "onSessionModifyResponseReceived status=" + status + " requestedProfile=" + requestedProfile + " responseProfile=" + responseProfile); if (status != VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS) { + // Report the reason the upgrade failed as the new session modification state. + if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) { + mCall.setSessionModificationState( + Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT); + } else { + if (status == VideoProvider.SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE) { + mCall.setSessionModificationState( + Call.SessionModificationState.REQUEST_REJECTED); + } else { + mCall.setSessionModificationState( + Call.SessionModificationState.REQUEST_FAILED); + } + } InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoFail(status, mCall); } else if (requestedProfile != null && responseProfile != null) { boolean modifySucceeded = requestedProfile.getVideoState() == @@ -95,6 +108,8 @@ public class InCallVideoCallCallback extends VideoCall.Callback { } else { Log.d(this, "onSessionModifyResponseReceived request and response Profiles are null"); } + // Finally clear the outstanding request. + mCall.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); } /** diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java index 86f997575..bc3687ced 100644 --- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java +++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java @@ -45,7 +45,9 @@ import com.android.incallui.InCallPresenter.InCallState; /** * This class adds Notifications to the status bar for the in-call experience. */ -public class StatusBarNotifier implements InCallPresenter.InCallStateListener { +public class StatusBarNotifier implements InCallPresenter.InCallStateListener, + CallList.CallUpdateListener { + // notification types private static final int IN_CALL_NOTIFICATION = 1; @@ -58,6 +60,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { private int mSavedContent = 0; private Bitmap mSavedLargeIcon; private String mSavedContentTitle; + private String mCallId = null; + private InCallState mInCallState; public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) { Preconditions.checkNotNull(context); @@ -74,7 +78,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { @Override public void onStateChange(InCallState oldState, InCallState newState, CallList callList) { Log.d(this, "onStateChange"); - + mInCallState = newState; updateNotification(newState, callList); } @@ -146,6 +150,12 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { final boolean isIncoming = (call.getState() == Call.State.INCOMING || call.getState() == Call.State.CALL_WAITING); + if (mCallId != null) { + CallList.getInstance().removeCallUpdateListener(mCallId, this); + } + mCallId = call.getId(); + CallList.getInstance().addCallUpdateListener(call.getId(), this); + // we make a call to the contact info cache to query for supplemental data to what the // call provides. This includes the contact name and photo. // This callback will always get called immediately and synchronously with whatever data @@ -574,4 +584,25 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { return PendingIntent.getBroadcast(context, 0, intent, 0); } + @Override + public void onCallChanged(Call call) { + // no-op + } + + /** + * Responds to changes in the session modification state for the call by dismissing the + * status bar notification as required. + * + * @param sessionModificationState The new session modification state. + */ + @Override + public void onSessionModificationStateChange(int sessionModificationState) { + if (sessionModificationState == Call.SessionModificationState.NO_REQUEST) { + if (mCallId != null) { + CallList.getInstance().removeCallUpdateListener(mCallId, this); + } + + updateNotification(mInCallState, CallList.getInstance()); + } + } } diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java index 6d70b4271..24e9e8d76 100644 --- a/InCallUI/src/com/android/incallui/VideoCallFragment.java +++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java @@ -659,10 +659,12 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter, params.height = height; preview.setLayoutParams(params); - ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams(); - containerParams.width = width; - containerParams.height = height; - mPreviewVideoContainer.setLayoutParams(containerParams); + if (mPreviewVideoContainer != null) { + ViewGroup.LayoutParams containerParams = mPreviewVideoContainer.getLayoutParams(); + containerParams.width = width; + containerParams.height = height; + mPreviewVideoContainer.setLayoutParams(containerParams); + } // The width and height are interchanged outside of this method based on the current // orientation, so we can transform using "width", which will be either the width or diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index f145c24ca..87843f907 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -22,7 +22,6 @@ import android.database.Cursor; import android.graphics.Point; import android.net.Uri; import android.os.AsyncTask; -import android.os.Handler; import android.provider.ContactsContract; import android.telecom.AudioState; import android.telecom.CameraCapabilities; @@ -31,6 +30,7 @@ import android.telecom.Connection.VideoProvider; import android.telecom.InCallService.VideoCall; import android.telecom.VideoProfile; import android.view.Surface; +import android.view.View; import android.widget.ImageView; import com.android.contacts.common.CallUtil; @@ -163,10 +163,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi private static boolean mIsVideoMode = false; - /** Handler which resets request state to NO_REQUEST after an interval. */ - private Handler mSessionModificationResetHandler; - private static final long SESSION_MODIFICATION_RESET_DELAY_MS = 3000; - /** * Contact photo manager to retrieve cached contact photo information. */ @@ -186,7 +182,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi mContext = context; mMinimumVideoDimension = mContext.getResources().getDimension( R.dimen.video_preview_small_dimension); - mSessionModificationResetHandler = new Handler(); } /** @@ -933,8 +928,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if (call == null) { return; } - - call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); } @Override @@ -947,25 +940,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi if (call == null) { return; } - - if (status == VideoProvider.SESSION_MODIFY_REQUEST_TIMED_OUT) { - call.setSessionModificationState( - Call.SessionModificationState.UPGRADE_TO_VIDEO_REQUEST_TIMED_OUT); - } else { - call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED); - - final Call modifyCall = call; - // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval. - mSessionModificationResetHandler.postDelayed(new Runnable() { - @Override - public void run() { - if (modifyCall != null) { - modifyCall - .setSessionModificationState(Call.SessionModificationState.NO_REQUEST); - } - } - }, SESSION_MODIFICATION_RESET_DELAY_MS); - } } @Override @@ -1162,7 +1136,11 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi } } } + return null; + } + @Override + protected void onPostExecute(Void result) { // If user profile information was found, issue an async request to load the user's // profile photo. if (mProfileInfo != null) { @@ -1174,17 +1152,14 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi new ContactPhotoManager.DefaultImageRequest(mProfileInfo.name, mProfileInfo.lookupKey, false /* isCircularPhoto */); - mContactPhotoManager - .loadDirectoryPhoto(ui.getPreviewPhotoView(), + ImageView photoView = ui.getPreviewPhotoView(); + if (photoView == null) { + return; + } + mContactPhotoManager.loadDirectoryPhoto(photoView, mProfileInfo.displayPhotoUri, false /* darkTheme */, false /* isCircular */, imageRequest); } - return null; - } - - @Override - protected void onPostExecute(Void result) { - // No-op } }; |