summaryrefslogtreecommitdiff
path: root/InCallUI/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'InCallUI/src/com/android')
-rw-r--r--InCallUI/src/com/android/incallui/AnswerFragment.java56
-rw-r--r--InCallUI/src/com/android/incallui/AnswerPresenter.java52
-rw-r--r--InCallUI/src/com/android/incallui/Call.java11
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonFragment.java117
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java28
-rw-r--r--InCallUI/src/com/android/incallui/CallCardFragment.java136
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java64
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java31
-rw-r--r--InCallUI/src/com/android/incallui/CallUtils.java5
-rw-r--r--InCallUI/src/com/android/incallui/GlowPadWrapper.java17
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java59
-rw-r--r--InCallUI/src/com/android/incallui/InCallVideoCallCallback.java15
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java58
-rw-r--r--InCallUI/src/com/android/incallui/VideoCallFragment.java138
-rw-r--r--InCallUI/src/com/android/incallui/VideoCallPresenter.java333
-rw-r--r--InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java8
16 files changed, 710 insertions, 418 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 33cdba4c0..9decb7b21 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -98,6 +98,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()
@@ -167,27 +178,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) {
@@ -221,15 +220,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);
}
}
@@ -267,17 +267,14 @@ public class AnswerPresenter extends Presenter<AnswerPresenter.AnswerUi>
}
private void configureAnswerTargetsForSms(Call call, List<String> textMsgs) {
- if (getUi() == null) {
- return;
- }
-
- final Context context = getUi().getContext();
-
mHasTextMessages = textMsgs != null;
boolean withSms =
call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT)
&& mHasTextMessages;
- if (call.isVideoCall(context)) {
+
+ // Only present the user with the option to answer as a video call if the incoming call is
+ // a bi-directional video call.
+ if (VideoProfile.VideoState.isBidirectional((call.getVideoState()))) {
if (withSms) {
getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
getUi().configureMessageDialog(textMsgs);
@@ -297,6 +294,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 5cf0f9b1a..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 {
@@ -509,11 +510,8 @@ public class Call {
mSessionModificationState = state;
Log.d(this, "setSessionModificationState " + state + " mSessionModificationState="
+ mSessionModificationState);
- if (state != Call.SessionModificationState.WAITING_FOR_RESPONSE) {
- setModifyToVideoState(VideoProfile.VideoState.AUDIO_ONLY);
- }
if (hasChanged) {
- update();
+ CallList.getInstance().onSessionModificationStateChange(this, state);
}
}
@@ -549,7 +547,7 @@ public class Call {
}
return String.format(Locale.US, "[%s, %s, %s, children:%s, parent:%s, conferenceable:%s, " +
- "videoState:%d, mSessionModificationState:%d, VideoSettings:%s]",
+ "videoState:%s, mSessionModificationState:%d, VideoSettings:%s]",
mId,
State.toString(getState()),
android.telecom.Call.Details
@@ -557,7 +555,8 @@ public class Call {
mChildCallIds,
getParentId(),
this.mTelecommCall.getConferenceableCalls(),
- mTelecommCall.getDetails().getVideoState(),
+ VideoProfile.VideoState.videoStateToString(
+ mTelecommCall.getDetails().getVideoState()),
mSessionModificationState,
getVideoSettings());
}
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index b3cadcfec..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
@@ -68,24 +62,22 @@ public class CallButtonFragment
public interface Buttons {
public static final int BUTTON_AUDIO = 0;
- public static final int BUTTON_DOWNGRADE_TO_VOICE = 1;
- public static final int BUTTON_MUTE = 2;
- public static final int BUTTON_DIALPAD = 3;
- public static final int BUTTON_HOLD = 4;
- public static final int BUTTON_SWAP = 5;
- public static final int BUTTON_UPGRADE_TO_VIDEO = 6;
- public static final int BUTTON_SWITCH_CAMERA = 7;
- public static final int BUTTON_ADD_CALL = 8;
- public static final int BUTTON_MERGE = 9;
- public static final int BUTTON_PAUSE_VIDEO = 10;
- public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 11;
- public static final int BUTTON_COUNT = 12;
+ public static final int BUTTON_MUTE = 1;
+ public static final int BUTTON_DIALPAD = 2;
+ public static final int BUTTON_HOLD = 3;
+ public static final int BUTTON_SWAP = 4;
+ public static final int BUTTON_UPGRADE_TO_VIDEO = 5;
+ public static final int BUTTON_SWITCH_CAMERA = 6;
+ public static final int BUTTON_ADD_CALL = 7;
+ public static final int BUTTON_MERGE = 8;
+ public static final int BUTTON_PAUSE_VIDEO = 9;
+ public static final int BUTTON_MANAGE_VIDEO_CONFERENCE = 10;
+ public static final int BUTTON_COUNT = 11;
}
private SparseIntArray mButtonVisibilityMap = new SparseIntArray(BUTTON_COUNT);
private CompoundButton mAudioButton;
- private ImageButton mChangeToVoiceButton;
private CompoundButton mMuteButton;
private CompoundButton mShowDialpadButton;
private CompoundButton mHoldButton;
@@ -129,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
@@ -138,8 +132,6 @@ public class CallButtonFragment
mAudioButton = (CompoundButton) parent.findViewById(R.id.audioButton);
mAudioButton.setOnClickListener(this);
- mChangeToVoiceButton = (ImageButton) parent.findViewById(R.id.changeToVoiceButton);
- mChangeToVoiceButton. setOnClickListener(this);
mMuteButton = (CompoundButton) parent.findViewById(R.id.muteButton);
mMuteButton.setOnClickListener(this);
mShowDialpadButton = (CompoundButton) parent.findViewById(R.id.dialpadButton);
@@ -196,10 +188,6 @@ public class CallButtonFragment
case R.id.addButton:
getPresenter().addCallClicked();
break;
- case R.id.changeToVoiceButton:
- // STOPSHIP One way video options
- getPresenter().displayModifyCallOptions();
- break;
case R.id.muteButton: {
getPresenter().muteClicked(!mMuteButton.isSelected());
break;
@@ -219,8 +207,7 @@ public class CallButtonFragment
getPresenter().showDialpadClicked(!mShowDialpadButton.isSelected());
break;
case R.id.changeToVideoButton:
- // STOPSHIP One way video options
- getPresenter().displayModifyCallOptions();
+ getPresenter().changeToVideoClicked();
break;
case R.id.switchCameraButton:
getPresenter().switchCameraClicked(
@@ -271,7 +258,6 @@ public class CallButtonFragment
}
ImageButton[] normalButtons = {
- mChangeToVoiceButton,
mSwapButton,
mChangeToVideoButton,
mAddCallButton,
@@ -358,7 +344,6 @@ public class CallButtonFragment
mIsEnabled = isEnabled;
mAudioButton.setEnabled(isEnabled);
- mChangeToVoiceButton.setEnabled(isEnabled);
mMuteButton.setEnabled(isEnabled);
mShowDialpadButton.setEnabled(isEnabled);
mHoldButton.setEnabled(isEnabled);
@@ -389,8 +374,6 @@ public class CallButtonFragment
switch (id) {
case BUTTON_AUDIO:
return mAudioButton;
- case BUTTON_DOWNGRADE_TO_VOICE:
- return mChangeToVoiceButton;
case BUTTON_MUTE:
return mMuteButton;
case BUTTON_DIALPAD:
@@ -444,74 +427,6 @@ public class CallButtonFragment
}
}
- /**The function is called when Modify Call button gets pressed. The function creates and
- * displays modify call options.
- */
- @Override
- public void displayModifyCallOptions() {
- CallButtonPresenter.CallButtonUi ui = getUi();
- if (ui == null) {
- Log.e(this, "Cannot display ModifyCallOptions as ui is null");
- return;
- }
-
- Context context = getContext();
-
- final ArrayList<CharSequence> items = new ArrayList<CharSequence>();
- final ArrayList<Integer> itemToCallType = new ArrayList<Integer>();
- final Resources res = ui.getContext().getResources();
- // Prepare the string array and mapping.
- items.add(res.getText(R.string.modify_call_option_voice));
- itemToCallType.add(VideoProfile.VideoState.AUDIO_ONLY);
-
- items.add(res.getText(R.string.modify_call_option_vt_rx));
- itemToCallType.add(VideoProfile.VideoState.RX_ENABLED);
-
- items.add(res.getText(R.string.modify_call_option_vt_tx));
- itemToCallType.add(VideoProfile.VideoState.TX_ENABLED);
-
- items.add(res.getText(R.string.modify_call_option_vt));
- itemToCallType.add(VideoProfile.VideoState.BIDIRECTIONAL);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(getUi().getContext());
- builder.setTitle(R.string.modify_call_option_title);
- final AlertDialog alert;
-
- DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int item) {
- Toast.makeText(getUi().getContext(), items.get(item), Toast.LENGTH_SHORT).show();
- final int selCallType = itemToCallType.get(item);
- Log.v(this, "Videocall: ModifyCall: upgrade/downgrade to "
- + fromCallType(selCallType));
- VideoProfile videoProfile = new VideoProfile(selCallType);
- getPresenter().changeToVideoClicked(videoProfile);
- dialog.dismiss();
- }
- };
- int currVideoState = getPresenter().getCurrentVideoState();
- int currUnpausedVideoState = CallUtils.getUnPausedVideoState(currVideoState);
- int index = itemToCallType.indexOf(currUnpausedVideoState);
- if (index == INVALID_INDEX) {
- return;
- }
- builder.setSingleChoiceItems(items.toArray(new CharSequence[0]), index, listener);
- alert = builder.create();
- alert.show();
- }
-
- public static String fromCallType(int callType) {
- switch (callType) {
- case VideoProfile.VideoState.BIDIRECTIONAL:
- return "VT";
- case VideoProfile.VideoState.TX_ENABLED:
- return "VT_TX";
- case VideoProfile.VideoState.RX_ENABLED:
- return "VT_RX";
- }
- return "";
- }
-
private void addToOverflowMenu(int id, View button, PopupMenu menu) {
button.setVisibility(View.GONE);
menu.getMenu().add(Menu.NONE, id, Menu.NONE, button.getContentDescription());
@@ -539,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 82c576f79..d788a1097 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -79,6 +79,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
InCallPresenter.getInstance().removeIncomingCallListener(this);
InCallPresenter.getInstance().removeDetailsListener(this);
InCallPresenter.getInstance().getInCallCameraManager().removeCameraSelectionListener(this);
+ InCallPresenter.getInstance().removeCanAddCallListener(this);
}
@Override
@@ -258,20 +259,16 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
getUi().displayDialpad(checked /* show */, true /* animate */);
}
- public void displayModifyCallOptions() {
- getUi().displayModifyCallOptions();
- }
-
- public int getCurrentVideoState() {
- return mCall.getVideoState();
- }
-
- public void changeToVideoClicked(VideoProfile videoProfile) {
+ public void changeToVideoClicked() {
VideoCall videoCall = mCall.getVideoCall();
if (videoCall == null) {
return;
}
+ int currVideoState = mCall.getVideoState();
+ int currUnpausedVideoState = CallUtils.getUnPausedVideoState(currVideoState);
+ currUnpausedVideoState |= VideoProfile.VideoState.BIDIRECTIONAL;
+ VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
videoCall.sendSessionModifyRequest(videoProfile);
mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
}
@@ -316,15 +313,16 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
if (pause) {
videoCall.setCamera(null);
VideoProfile videoProfile = new VideoProfile(
- mCall.getVideoState() | VideoProfile.VideoState.PAUSED);
+ mCall.getVideoState() & ~VideoProfile.VideoState.TX_ENABLED);
videoCall.sendSessionModifyRequest(videoProfile);
} else {
InCallCameraManager cameraManager = InCallPresenter.getInstance().
getInCallCameraManager();
videoCall.setCamera(cameraManager.getActiveCameraId());
VideoProfile videoProfile = new VideoProfile(
- mCall.getVideoState() & ~VideoProfile.VideoState.PAUSED);
+ mCall.getVideoState() | VideoProfile.VideoState.TX_ENABLED);
videoCall.sendSessionModifyRequest(videoProfile);
+ mCall.setSessionModificationState(Call.SessionModificationState.WAITING_FOR_RESPONSE);
}
getUi().setVideoPaused(pause);
}
@@ -354,7 +352,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
* @param call The active call.
*/
private void updateButtonsState(Call call) {
- Log.v(this, "Showing buttons for voice call.");
+ Log.v(this, "updateButtonsState");
final CallButtonUi ui = getUi();
final boolean isVideo = CallUtils.isVideoCall(call);
@@ -373,9 +371,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
final boolean showAddCall = TelecomAdapter.getInstance().canAddCall();
final boolean showMerge = call.can(
android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE);
- // TODO: This button is currently being used for both upgrade and downgrade scenarios.
- // It should be split into BUTTON_DOWNGRADE_TO_VOICE AND BUTTON_UPGRADE_TO_VIDEO
- final boolean showUpgradeToVideo = isVideo ||
+ final boolean showUpgradeToVideo = !isVideo &&
(call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_LOCAL_TX)
&& call.can(android.telecom.Call.Details.CAPABILITY_SUPPORTS_VT_REMOTE_RX));
@@ -388,7 +384,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
ui.showButton(BUTTON_MUTE, showMute);
ui.showButton(BUTTON_ADD_CALL, showAddCall);
ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
- ui.showButton(BUTTON_DOWNGRADE_TO_VOICE, false);
ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
ui.showButton(BUTTON_DIALPAD, !isVideo);
@@ -436,7 +431,6 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
void setAudio(int mode);
void setSupportedAudio(int mask);
void displayDialpad(boolean on, boolean animate);
- void displayModifyCallOptions();
boolean isDialpadVisible();
/**
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 691589ca0..499f46417 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(
@@ -269,6 +311,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
// Retrieve here since at fragment creation time the incoming video view is not inflated.
final View videoView = getView().findViewById(R.id.incomingVideo);
+ if (videoView == null) {
+ return;
+ }
// Determine how much space there is below or to the side of the call card.
final float spaceBesideCallCard = getSpaceBesideCallCard();
@@ -492,15 +537,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 +556,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 +573,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 +604,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 +612,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 +749,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 +757,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 +769,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 +806,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 +834,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..bec81ec57 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) {
@@ -501,7 +516,12 @@ public class CallList {
*/
public void notifyCallsOfDeviceRotation(int rotation) {
for (Call call : mCallById.values()) {
- if (call.getVideoCall() != null) {
+ // First, ensure a VideoCall is set on the call so that the change can be sent to the
+ // provider (a VideoCall can be present for a call that does not currently have video,
+ // but can be upgraded to video).
+ // Second, ensure that the call videoState has video enabled (there is no need to set
+ // device orientation on a voice call which has not yet been upgraded to video).
+ if (call.getVideoCall() != null && CallUtils.isVideoCall(call)) {
call.getVideoCall().setDeviceOrientation(rotation);
}
}
@@ -556,10 +576,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/CallUtils.java b/InCallUI/src/com/android/incallui/CallUtils.java
index 79cf4c7e7..0e0c4c6f5 100644
--- a/InCallUI/src/com/android/incallui/CallUtils.java
+++ b/InCallUI/src/com/android/incallui/CallUtils.java
@@ -39,9 +39,8 @@ public class CallUtils {
}
public static boolean isVideoCall(int videoState) {
- return VideoProfile.VideoState.isBidirectional(videoState)
- && VideoProfile.VideoState.isTransmissionEnabled(videoState)
- && VideoProfile.VideoState.isReceptionEnabled(videoState);
+ return VideoProfile.VideoState.isTransmissionEnabled(videoState)
+ || VideoProfile.VideoState.isReceptionEnabled(videoState);
}
public static boolean isIncomingVideoCall(Call call) {
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/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 5ecb96e3f..289bf6388 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -24,19 +24,21 @@ import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
-import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.Trace;
import android.telecom.DisconnectCause;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
+import android.view.Display;
import android.view.MenuItem;
+import android.view.OrientationEventListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.KeyEvent;
@@ -122,10 +124,13 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
}
};
+ /** Listener for orientation changes. */
+ private OrientationEventListener mOrientationEventListener;
+
/**
- * Used to determine if a change in orientation has occurred.
+ * Used to determine if a change in rotation has occurred.
*/
- private static int sCurrentOrientation = Configuration.ORIENTATION_UNDEFINED;
+ private static int sPreviousRotation = -1;
@Override
protected void onCreate(Bundle icicle) {
@@ -193,6 +198,28 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
dialogFragment.setListener(mSelectAcctListener);
}
}
+
+ mOrientationEventListener = new OrientationEventListener(this,
+ SensorManager.SENSOR_DELAY_NORMAL) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ // Orientation is the current device orientation in degrees. Ultimately we want
+ // the rotation (in fixed 90 degree intervals).
+ Display display = getWindowManager().getDefaultDisplay();
+ int newRotation = display.getRotation();
+ if (newRotation != sPreviousRotation) {
+ doOrientationChanged(newRotation);
+ }
+ }
+ };
+
+ if (mOrientationEventListener.canDetectOrientation()) {
+ Log.v(this, "Orientation detection enabled.");
+ mOrientationEventListener.enable();
+ } else {
+ Log.v(this, "Orientation detection disabled.");
+ mOrientationEventListener.disable();
+ }
Log.d(this, "onCreate(): exit");
}
@@ -215,9 +242,6 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
// setting activity should be last thing in setup process
InCallPresenter.getInstance().setActivity(this);
- // It is possible that the activity restarted because orientation changed.
- // Notify listeners if orientation changed.
- doOrientationChanged(getResources().getConfiguration().orientation);
InCallPresenter.getInstance().onActivityStarted();
}
@@ -357,7 +381,7 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
// in-call UI:
if ((mConferenceManagerFragment == null || !mConferenceManagerFragment.isVisible())
- && !mCallCardFragment.isVisible()) {
+ && (mCallCardFragment == null || !mCallCardFragment.isVisible())) {
return;
}
@@ -474,20 +498,19 @@ public class InCallActivity extends Activity implements FragmentDisplayManager {
}
/**
- * Handles changes in device orientation.
+ * Handles changes in device rotation.
*
- * @param orientation The new device orientation.
+ * @param rotation The new device rotation.
*/
- private void doOrientationChanged(int orientation) {
- Log.d(this, "doOrientationChanged prevOrientation=" + sCurrentOrientation +
- " newOrientation=" + orientation);
- // Check to see if the orientation changed to prevent triggering orientation change events
+ private void doOrientationChanged(int rotation) {
+ Log.d(this, "doOrientationChanged prevOrientation=" + sPreviousRotation +
+ " newOrientation=" + rotation);
+ // Check to see if the rotation changed to prevent triggering rotation change events
// for other configuration changes.
- if (orientation != sCurrentOrientation) {
- sCurrentOrientation = orientation;
- InCallPresenter.getInstance().onDeviceRotationChange(
- getWindowManager().getDefaultDisplay().getRotation());
- InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
+ if (rotation != sPreviousRotation) {
+ sPreviousRotation = rotation;
+ InCallPresenter.getInstance().onDeviceRotationChange(rotation);
+ InCallPresenter.getInstance().onDeviceOrientationChange(sPreviousRotation);
}
}
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..a54536ce6 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
@@ -187,7 +197,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
// Check if data has changed; if nothing is different, don't issue another notification.
final int iconResId = getIconToDisplay(call);
- final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
+ Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
final int contentResId = getContentString(call);
final String contentTitle = getContentTitle(contactInfo, call);
@@ -195,6 +205,10 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
return;
}
+ if (largeIcon != null) {
+ largeIcon = getRoundedIcon(largeIcon);
+ }
+
/*
* Nothing more to check...build and send it.
*/
@@ -342,15 +356,18 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
}
+ return largeIcon;
+ }
- if (largeIcon != null) {
- final int height = (int) mContext.getResources().getDimension(
- android.R.dimen.notification_large_icon_height);
- final int width = (int) mContext.getResources().getDimension(
- android.R.dimen.notification_large_icon_width);
- largeIcon = BitmapUtil.getRoundedBitmap(largeIcon, width, height);
+ private Bitmap getRoundedIcon(Bitmap bitmap) {
+ if (bitmap == null) {
+ return null;
}
- return largeIcon;
+ final int height = (int) mContext.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ final int width = (int) mContext.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ return BitmapUtil.getRoundedBitmap(bitmap, width, height);
}
/**
@@ -574,4 +591,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 c285ff3fe..24e9e8d76 100644
--- a/InCallUI/src/com/android/incallui/VideoCallFragment.java
+++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java
@@ -16,15 +16,10 @@
package com.android.incallui;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
-import android.telecom.Connection;
-import android.telecom.VideoProfile;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.Surface;
@@ -33,7 +28,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
-import android.widget.Toast;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import com.google.common.base.Objects;
@@ -65,12 +61,6 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
*/
public static final int ORIENTATION_UNKNOWN = -1;
- /**
- * Invalid resource id.
- */
- public static final int INVALID_RESOURCE_ID = -1;
-
-
// Static storage used to retain the video surfaces across Activity restart.
// TextureViews are not parcelable, so it is not possible to store them in the saved state.
private static boolean sVideoSurfacesInUse = false;
@@ -91,6 +81,21 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
private View mVideoViews;
/**
+ * The {@link FrameLayout} containing the preview surface.
+ */
+ private View mPreviewVideoContainer;
+
+ /**
+ * Icon shown to indicate that the outgoing camera has been turned off.
+ */
+ private View mCameraOff;
+
+ /**
+ * {@link ImageView} containing the user's profile photo.
+ */
+ private ImageView mPreviewPhoto;
+
+ /**
* {@code True} when the layout of the activity has been completed.
*/
private boolean mIsLayoutComplete = false;
@@ -534,19 +539,21 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
}
/**
- * Show or hide preview and incoming video views
+ * Hides and shows the incoming video view and changes the outgoing video view's state based on
+ * whether outgoing view is enabled or not.
*/
- public void showVideoViews(boolean showPreview, boolean showIncoming) {
+ public void showVideoViews(boolean previewPaused, boolean showIncoming) {
inflateVideoUi(true);
View incomingVideoView = mVideoViews.findViewById(R.id.incomingVideo);
- View previewVideoView = mVideoViews.findViewById(R.id.previewVideo);
-
if (incomingVideoView != null) {
incomingVideoView.setVisibility(showIncoming ? View.VISIBLE : View.INVISIBLE);
}
- if (previewVideoView != null) {
- previewVideoView.setVisibility(showPreview ? View.VISIBLE : View.INVISIBLE);
+ if (mCameraOff != null) {
+ mCameraOff.setVisibility(!previewPaused ? View.VISIBLE : View.INVISIBLE);
+ }
+ if (mPreviewPhoto != null) {
+ mPreviewPhoto.setVisibility(!previewPaused ? View.VISIBLE : View.INVISIBLE);
}
}
@@ -558,43 +565,6 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
}
/**
- * Displays a message on the UI that the video call quality has changed.
- *
- */
- @Override
- public void showVideoQualityChanged(int videoQuality) {
- Log.d(this, "showVideoQualityChanged. Video quality changed to " + videoQuality);
-
- final Context context = getActivity();
- if (context == null) {
- Log.e(this, "showVideoQualityChanged - Activity is null. Return");
- return;
- }
-
- final Resources resources = context.getResources();
-
- int videoQualityResourceId = R.string.video_quality_unknown;
- switch (videoQuality) {
- case VideoProfile.QUALITY_HIGH:
- videoQualityResourceId = R.string.video_quality_high;
- break;
- case VideoProfile.QUALITY_MEDIUM:
- videoQualityResourceId = R.string.video_quality_medium;
- break;
- case VideoProfile.QUALITY_LOW:
- videoQualityResourceId = R.string.video_quality_low;
- break;
- default:
- break;
- }
-
- String videoQualityChangedText = resources.getString(R.string.video_quality_changed) +
- resources.getString(videoQualityResourceId);
-
- Toast.makeText(context, videoQualityChangedText, Toast.LENGTH_SHORT).show();
- }
-
- /**
* Cleans up the video telephony surfaces. Used when the presenter indicates a change to an
* audio-only state. Since the surfaces are static, it is important to ensure they are cleaned
* up promptly.
@@ -613,6 +583,11 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
sVideoSurfacesInUse = false;
}
+ @Override
+ public ImageView getPreviewPhotoView() {
+ return mPreviewPhoto;
+ }
+
private void onPresenterChanged(VideoCallPresenter presenter) {
Log.d(this, "onPresenterChanged: Presenter=" + presenter);
if (sDisplaySurface != null) {
@@ -678,11 +653,19 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
return;
}
+ // Set the dimensions of both the video surface and the FrameLayout containing it.
ViewGroup.LayoutParams params = preview.getLayoutParams();
params.width = width;
params.height = height;
preview.setLayoutParams(params);
+ 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
// the height.
@@ -739,47 +722,6 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
}
/**
- * Sets the call's data usage value
- *
- * @param context the current context
- * @param dataUsage the data usage value
- */
- @Override
- public void setCallDataUsage(Context context, long dataUsage) {
- Log.d(this, "setDataUsage: dataUsage = " + dataUsage);
- Toast.makeText(context, "dataUsage=" + dataUsage, Toast.LENGTH_LONG).show();
- }
-
- private int fromCallSessionEvent(int event) {
- switch (event) {
- case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
- return R.string.player_stopped;
- case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
- return R.string.player_started;
- case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
- return R.string.camera_not_ready;
- case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
- return R.string.camera_ready;
- default:
- return R.string.unknown_call_session_event;
- }
- }
-
- /**
- * Sets the call's data usage value
- *
- * @param context the current context
- * @param event the call session event
- */
- @Override
- public void displayCallSessionEvent(int event) {
- Log.d(this, "displayCallSessionEvent: event = " + event);
- Context context = getActivity();
- String msg = context.getResources().getString(fromCallSessionEvent(event));
- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
- }
-
- /**
* Determines the size of the device screen.
*
* @return {@link Point} specifying the width and height of the screen.
@@ -818,6 +760,10 @@ public class VideoCallFragment extends BaseFragment<VideoCallPresenter,
}
if (mVideoViews != null) {
+ mPreviewVideoContainer = mVideoViews.findViewById(R.id.previewVideoContainer);
+ mCameraOff = mVideoViews.findViewById(R.id.previewCameraOff);
+ mPreviewPhoto = (ImageView) mVideoViews.findViewById(R.id.previewProfilePhoto);
+
TextureView displaySurface = (TextureView) mVideoViews.findViewById(R.id.incomingVideo);
Log.d(this, "inflateVideoCallViews: sVideoSurfacesInUse=" + sVideoSurfacesInUse);
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index f11f328e6..ba82c01fc 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -18,8 +18,13 @@ package com.android.incallui;
import android.content.Context;
import android.content.res.Configuration;
+import android.database.Cursor;
import android.graphics.Point;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Handler;
+import android.os.Looper;
+import android.provider.ContactsContract;
import android.telecom.AudioState;
import android.telecom.CameraCapabilities;
import android.telecom.Connection;
@@ -27,15 +32,17 @@ 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;
+import com.android.contacts.common.ContactPhotoManager;
import com.android.incallui.InCallPresenter.InCallDetailsListener;
import com.android.incallui.InCallPresenter.InCallOrientationListener;
import com.android.incallui.InCallPresenter.InCallStateListener;
import com.android.incallui.InCallPresenter.IncomingCallListener;
import com.android.incallui.InCallVideoCallCallbackNotifier.SurfaceChangeListener;
import com.android.incallui.InCallVideoCallCallbackNotifier.VideoEventListener;
-import com.google.common.base.Preconditions;
import java.util.Objects;
@@ -70,6 +77,22 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
public static final boolean DEBUG = false;
/**
+ * Runnable which is posted to schedule automatically entering fullscreen mode.
+ */
+ private Runnable mAutoFullscreenRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mAutoFullScreenPending) {
+ Log.v(this, "Automatically entering fullscreen mode.");
+ setFullScreen(true);
+ mAutoFullScreenPending = false;
+ } else {
+ Log.v(this, "Skipping scheduled fullscreen mode.");
+ }
+ }
+ };
+
+ /**
* Determines the device orientation (portrait/lanscape).
*/
public int getDeviceOrientation() {
@@ -158,9 +181,38 @@ 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.
+ */
+ private ContactPhotoManager mContactPhotoManager = null;
+
+ /**
+ * The URI for the user's profile photo, or {@code null} if not specified.
+ */
+ private ContactInfoCache.ContactCacheEntry mProfileInfo = null;
+
+ /**
+ * UI thread handler used for delayed task execution.
+ */
+ private Handler mHandler;
+
+ /**
+ * Determines whether video calls should automatically enter full screen mode after
+ * {@link #mAutoFullscreenTimeoutMillis} milliseconds.
+ */
+ private boolean mIsAutoFullscreenEnabled = false;
+
+ /**
+ * Determines the number of milliseconds after which a video call will automatically enter
+ * fullscreen mode. Requires {@link #mIsAutoFullscreenEnabled} to be {@code true}.
+ */
+ private int mAutoFullscreenTimeoutMillis = 0;
+
+ /**
+ * Determines if the countdown is currently running to automatically enter full screen video
+ * mode.
+ */
+ private boolean mAutoFullScreenPending = false;
/**
* Initializes the presenter.
@@ -168,10 +220,14 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
* @param context The current context.
*/
public void init(Context context) {
- mContext = Preconditions.checkNotNull(context);
+ mContext = context;
mMinimumVideoDimension = mContext.getResources().getDimension(
R.dimen.video_preview_small_dimension);
- mSessionModificationResetHandler = new Handler();
+ mHandler = new Handler(Looper.getMainLooper());
+ mIsAutoFullscreenEnabled = mContext.getResources()
+ .getBoolean(R.bool.video_call_auto_fullscreen);
+ mAutoFullscreenTimeoutMillis = mContext.getResources().getInteger(
+ R.integer.video_call_auto_fullscreen_timeout);
}
/**
@@ -315,6 +371,26 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
private void toggleFullScreen() {
mIsFullScreen = !mIsFullScreen;
+ Log.v(this, "toggleFullScreen = " + mIsFullScreen);
+ // Ensure we cancel any scheduled auto activation of fullscreen mode as the user's setting
+ // should override any automatic operation.
+ cancelAutoFullScreen();
+ InCallPresenter.getInstance().setFullScreenVideoState(mIsFullScreen);
+ }
+
+ /**
+ * Sets the current full screen mode.
+ *
+ * @param isFullScreen {@code true} if full screen mode should be active, {@code false}
+ * otherwise.
+ */
+ public void setFullScreen(boolean isFullScreen) {
+ Log.v(this, "setFullScreen = " + isFullScreen);
+ if (mIsFullScreen == isFullScreen) {
+ Log.v(this, "setFullScreen ignored as already in that state.");
+ return;
+ }
+ mIsFullScreen = isFullScreen;
InCallPresenter.getInstance().setFullScreenVideoState(mIsFullScreen);
}
@@ -367,21 +443,29 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
// Determine the primary active call).
Call primary = null;
+
+ // Determine the call which is the focus of the user's attention. In the case of an
+ // incoming call waiting call, the primary call is still the active video call, however
+ // the determination of whether we should be in fullscreen mode is based on the type of the
+ // incoming call, not the active video call.
+ Call currentCall = null;
+
if (newState == InCallPresenter.InCallState.INCOMING) {
// We don't want to replace active video call (primary call)
// with a waiting call, since user may choose to ignore/decline the waiting call and
// this should have no impact on current active video call, that is, we should not
// change the camera or UI unless the waiting VT call becomes active.
primary = callList.getActiveCall();
+ currentCall = callList.getIncomingCall();
if (!CallUtils.isActiveVideoCall(primary)) {
primary = callList.getIncomingCall();
}
} else if (newState == InCallPresenter.InCallState.OUTGOING) {
- primary = callList.getOutgoingCall();
+ currentCall = primary = callList.getOutgoingCall();
} else if (newState == InCallPresenter.InCallState.PENDING_OUTGOING) {
- primary = callList.getPendingOutgoingCall();
+ currentCall = primary = callList.getPendingOutgoingCall();
} else if (newState == InCallPresenter.InCallState.INCALL) {
- primary = callList.getActiveCall();
+ currentCall = primary = callList.getActiveCall();
}
final boolean primaryChanged = !Objects.equals(mPrimaryCall, primary);
@@ -390,10 +474,17 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
Log.d(this, "onStateChange mPrimaryCall = " + mPrimaryCall);
if (primaryChanged) {
onPrimaryCallChanged(primary);
- } else if(mPrimaryCall!=null) {
+ } else if (mPrimaryCall != null) {
updateVideoCall(primary);
}
updateCallCache(primary);
+
+ // If the call context changed, potentially exit fullscreen or schedule auto enter of
+ // fullscreen mode.
+ // If the current call context is no longer a video call, exit fullscreen mode.
+ maybeExitFullscreen(currentCall);
+ // Schedule auto-enter of fullscreen mode if the current call context is a video call
+ maybeAutoEnterFullscreen(currentCall);
}
private void checkForVideoStateChange(Call call) {
@@ -401,8 +492,10 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall
- + " hasVideoStateChanged=" +
- hasVideoStateChanged + " isVideoMode=" + isVideoMode());
+ + " hasVideoStateChanged=" + hasVideoStateChanged + " isVideoMode="
+ + isVideoMode() + " previousVideoState: " +
+ VideoProfile.VideoState.videoStateToString(mCurrentVideoState) + " newVideoState: "
+ + VideoProfile.VideoState.videoStateToString(call.getVideoState()));
if (!hasVideoStateChanged) {
return;
@@ -610,6 +703,8 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
updateAudioMode(true);
mIsVideoMode = true;
+
+ maybeAutoEnterFullscreen(call);
}
//TODO: Move this into Telecom. InCallUI should not be this close to audio functionality.
@@ -624,7 +719,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
sPrevVideoAudioMode != AudioModeProvider.AUDIO_MODE_INVALID;
Log.d(this, "Is previous audio mode valid = " + isPrevAudioModeValid + " enableSpeaker is "
- + enableSpeaker);
+ + enableSpeaker);
// Set audio mode to previous mode if enableSpeaker is false.
if (isPrevAudioModeValid && !enableSpeaker) {
@@ -682,9 +777,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
enableCamera(mVideoCall, false);
Log.d(this, "exitVideoMode mIsFullScreen: " + mIsFullScreen);
- if (mIsFullScreen) {
- toggleFullScreen();
- }
+ setFullScreen(false);
mIsVideoMode = false;
}
@@ -712,6 +805,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
ui.showVideoViews(true, false);
} else if (VideoProfile.VideoState.isReceptionEnabled(videoState)) {
ui.showVideoViews(false, !isPaused && isCallActive);
+ loadProfilePhotoAsync();
} else {
ui.hideVideoUi();
}
@@ -770,18 +864,7 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
*/
@Override
public void onVideoQualityChanged(Call call, int videoQuality) {
- if (!call.equals(mPrimaryCall)) {
- return;
- }
-
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "Error VideoCallUi is null. Return.");
- return;
- }
-
- // Display a video quality changed message on UI.
- ui.showVideoQualityChanged(videoQuality);
+ // No-op
}
/**
@@ -851,13 +934,28 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
*/
@Override
public void onCallSessionEvent(int event) {
- Log.d(this, "onCallSessionEvent event =" + event);
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "onCallSessionEvent: VideoCallUi is null");
- return;
+ StringBuilder sb = new StringBuilder();
+ sb.append("onCallSessionEvent = ");
+
+ switch (event) {
+ case Connection.VideoProvider.SESSION_EVENT_RX_PAUSE:
+ sb.append("rx_pause");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_RX_RESUME:
+ sb.append("rx_resume");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_FAILURE:
+ sb.append("camera_failure");
+ break;
+ case Connection.VideoProvider.SESSION_EVENT_CAMERA_READY:
+ sb.append("camera_ready");
+ break;
+ default:
+ sb.append("unknown event = ");
+ sb.append(event);
+ break;
}
- ui.displayCallSessionEvent(event);
+ Log.d(this, sb.toString());
}
/**
@@ -868,12 +966,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
@Override
public void onCallDataUsageChange(long dataUsage) {
Log.d(this, "onCallDataUsageChange dataUsage=" + dataUsage);
- VideoCallUi ui = getUi();
- if (ui == null) {
- Log.e(this, "onCallDataUsageChange: VideoCallUi is null");
- return;
- }
- ui.setCallDataUsage(mContext, dataUsage);
}
/**
@@ -917,8 +1009,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
if (call == null) {
return;
}
-
- call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST);
}
@Override
@@ -931,25 +1021,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
@@ -991,7 +1062,6 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
* @param width peer width
* @param height peer height
*/
-
private void setDisplayVideoSize(int width, int height) {
Log.d(this, "setDisplayVideoSize:Received peer width=" + width + " peer height=" + height);
VideoCallUi ui = getUi();
@@ -1013,6 +1083,65 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
ui.setDisplayVideoSize(size.x, size.y);
}
+ /**
+ * Exits fullscreen mode if the current call context has changed to a non-video call.
+ *
+ * @param call The call.
+ */
+ protected void maybeExitFullscreen(Call call) {
+ if (call == null) {
+ return;
+ }
+
+ if (!CallUtils.isVideoCall(call) || call.getState() == Call.State.INCOMING) {
+ setFullScreen(false);
+ }
+ }
+
+ /**
+ * Schedules auto-entering of fullscreen mode.
+ * Will not enter full screen mode if any of the following conditions are met:
+ * 1. No call
+ * 2. Call is not active
+ * 3. Call is not video call
+ * 4. Already in fullscreen mode
+ *
+ * @param call The current call.
+ */
+ protected void maybeAutoEnterFullscreen(Call call) {
+ if (!mIsAutoFullscreenEnabled) {
+ return;
+ }
+
+ if (call == null || (
+ call != null && (call.getState() != Call.State.ACTIVE ||
+ !CallUtils.isVideoCall(call)) || mIsFullScreen)) {
+ // Ensure any previously scheduled attempt to enter fullscreen is cancelled.
+ cancelAutoFullScreen();
+ return;
+ }
+
+ if (mAutoFullScreenPending) {
+ Log.v(this, "maybeAutoEnterFullscreen : already pending.");
+ return;
+ }
+ Log.v(this, "maybeAutoEnterFullscreen : scheduled");
+ mAutoFullScreenPending = true;
+ mHandler.postDelayed(mAutoFullscreenRunnable, mAutoFullscreenTimeoutMillis);
+ }
+
+ /**
+ * Cancels pending auto fullscreen mode.
+ */
+ public void cancelAutoFullScreen() {
+ if (!mAutoFullScreenPending) {
+ Log.v(this, "cancelAutoFullScreen : none pending.");
+ return;
+ }
+ Log.v(this, "cancelAutoFullScreen : cancelling pending");
+ mAutoFullScreenPending = false;
+ }
+
private static boolean isAudioRouteEnabled(int audioRoute, int audioRouteMask) {
return ((audioRoute & audioRouteMask) != 0);
}
@@ -1101,12 +1230,87 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
}
/**
+ * Starts an asynchronous load of the user's profile photo.
+ */
+ public void loadProfilePhotoAsync() {
+ final VideoCallUi ui = getUi();
+ if (ui == null) {
+ return;
+ }
+
+ final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
+ /**
+ * Performs asynchronous load of the user profile information.
+ *
+ * @param params The parameters of the task.
+ *
+ * @return {@code null}.
+ */
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (mProfileInfo == null) {
+ // Try and read the photo URI from the local profile.
+ mProfileInfo = new ContactInfoCache.ContactCacheEntry();
+ final Cursor cursor = mContext.getContentResolver().query(
+ ContactsContract.Profile.CONTENT_URI, new String[]{
+ ContactsContract.CommonDataKinds.Phone._ID,
+ ContactsContract.CommonDataKinds.Phone.PHOTO_URI,
+ ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY,
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
+ }, null, null, null);
+ if (cursor != null) {
+ try {
+ if (cursor.moveToFirst()) {
+ mProfileInfo.lookupKey = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY));
+ String photoUri = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
+ mProfileInfo.displayPhotoUri = photoUri == null ? null
+ : Uri.parse(photoUri);
+ mProfileInfo.name = cursor.getString(cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ 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) {
+ if (mContactPhotoManager == null) {
+ mContactPhotoManager = ContactPhotoManager.getInstance(mContext);
+ }
+ ContactPhotoManager.DefaultImageRequest imageRequest = (mProfileInfo != null)
+ ? null :
+ new ContactPhotoManager.DefaultImageRequest(mProfileInfo.name,
+ mProfileInfo.lookupKey, false /* isCircularPhoto */);
+
+ ImageView photoView = ui.getPreviewPhotoView();
+ if (photoView == null) {
+ return;
+ }
+ mContactPhotoManager.loadDirectoryPhoto(photoView,
+ mProfileInfo.displayPhotoUri,
+ false /* darkTheme */, false /* isCircular */, imageRequest);
+ }
+ }
+ };
+
+ task.execute();
+ }
+
+ /**
* Defines the VideoCallUI interactions.
*/
public interface VideoCallUi extends Ui {
void showVideoViews(boolean showPreview, boolean showIncoming);
void hideVideoUi();
- void showVideoQualityChanged(int videoQuality);
boolean isDisplayVideoSurfaceCreated();
boolean isPreviewVideoSurfaceCreated();
Surface getDisplayVideoSurface();
@@ -1115,10 +1319,9 @@ public class VideoCallPresenter extends Presenter<VideoCallPresenter.VideoCallUi
void setPreviewSize(int width, int height);
void setPreviewSurfaceSize(int width, int height);
void setDisplayVideoSize(int width, int height);
- void setCallDataUsage(Context context, long dataUsage);
- void displayCallSessionEvent(int event);
Point getScreenSize();
Point getPreviewSize();
void cleanupSurfaces();
+ ImageView getPreviewPhotoView();
}
}
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
index 812e883c0..4bd27db9c 100644
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
+++ b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
@@ -637,11 +637,15 @@ public class GlowPadView extends View {
}
/**
- * Sets teh handle drawable to the drawable specified by the resource ID.
+ * Sets the handle drawable to the drawable specified by the resource ID.
* @param resourceId
*/
public void setHandleDrawable(int resourceId) {
- mHandleDrawable = new TargetDrawable(getResources(), resourceId, 2);
+ if (mHandleDrawable != null) {
+ mHandleDrawable.setDrawable(getResources(), resourceId);
+ } else {
+ mHandleDrawable = new TargetDrawable(getResources(), resourceId, 1);
+ }
mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
}