Merge "Use ContactsUtil.FLAG_N_FEATURE in CallerInfoAsyncQuery" into ub-contactsdialer-b-dev
diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml
index e247f0a..e3b89b9 100644
--- a/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml
+++ b/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml
@@ -28,7 +28,7 @@
<bitmap
android:gravity="center"
android:src="@drawable/ic_toolbar_video_off"
- android:tint="@color/glowpad_call_widget_normal_tint"
+ android:tint="@color/glowpad_end_call_widget_normal_tint"
android:autoMirrored="true" />
</item>
</layer-list>
diff --git a/InCallUI/res/layout-land/video_call_views.xml b/InCallUI/res/layout-land/video_call_views.xml
deleted file mode 100644
index 8961ea4..0000000
--- a/InCallUI/res/layout-land/video_call_views.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2014 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <TextureView
- android:id="@+id/incomingVideo"
- android:layout_gravity="center"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <!-- The width and height are replaced at runtime based on the selected camera. -->
- <TextureView
- android:id="@+id/previewVideo"
- android:layout_gravity="bottom|right"
- android:layout_margin="@dimen/video_preview_margin"
- android:layout_width="70dp"
- android:layout_height="120dp" />
-</FrameLayout>
diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml
index 7877ec8..8744d3e 100644
--- a/InCallUI/res/values/array.xml
+++ b/InCallUI/res/values/array.xml
@@ -76,10 +76,10 @@
<item>@drawable/ic_lockscreen_answer_video</item>
</array>
<array name="incoming_call_widget_video_without_sms_target_descriptions">
- <item>@string/description_target_answer_video_call</item>
+ <item>@string/description_target_answer_audio_call</item>
<item>@null</item>
<item>@string/description_target_decline</item>
- <item>@string/description_target_answer_audio_call</item>
+ <item>@string/description_target_answer_video_call</item>
</array>
<array name="incoming_call_widget_video_without_sms_direction_descriptions">
<item>@string/description_direction_right</item>
@@ -89,21 +89,21 @@
</array>
<!-- For video calls, if respond via SMS is enabled:
- - Answer as video call (drag right)
+ - Answer as audio call (drag right)
- Respond via SMS (drag up)
- Decline (drag left)
- - Answer as audio call (drag down) -->
+ - Answer as video call (drag down) -->
<array name="incoming_call_widget_video_with_sms_targets">
- <item>@drawable/ic_lockscreen_answer_video</item>
+ <item>@drawable/ic_lockscreen_answer</item>
<item>@drawable/ic_lockscreen_text</item>
<item>@drawable/ic_lockscreen_decline</item>
- <item>@drawable/ic_lockscreen_answer</item>
+ <item>@drawable/ic_lockscreen_answer_video</item>
</array>
<array name="incoming_call_widget_video_with_sms_target_descriptions">
- <item>@string/description_target_answer_video_call</item>
+ <item>@string/description_target_answer_audio_call</item>
<item>@string/description_target_send_sms</item>
<item>@string/description_target_decline</item>
- <item>@string/description_target_answer_audio_call</item>
+ <item>@string/description_target_answer_video_call</item>
</array>
<array name="incoming_call_widget_video_with_sms_direction_descriptions">
<item>@string/description_direction_right</item>
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 02dbfca..76ca4a9 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -48,6 +48,7 @@
@Override
public void onUiShowing(boolean showing) {
if (showing) {
+ CallList.getInstance().addListener(this);
final CallList calls = CallList.getInstance();
Call call;
call = calls.getIncomingCall();
@@ -60,6 +61,7 @@
processVideoUpgradeRequestCall(call);
}
} else {
+ CallList.getInstance().removeListener(this);
// This is necessary because the activity can be destroyed while an incoming call exists.
// This happens when back button is pressed while incoming call is still being shown.
if (mCallId != null) {
@@ -174,7 +176,7 @@
CallList.getInstance().addCallUpdateListener(mCallId, this);
final int currentVideoState = call.getVideoState();
- final int modifyToVideoState = call.getModifyToVideoState();
+ final int modifyToVideoState = call.getRequestedVideoState();
if (currentVideoState == modifyToVideoState) {
Log.w(this, "processVideoUpgradeRequestCall: Video states are same. Return.");
@@ -280,7 +282,7 @@
// Only present the user with the option to answer as a video call if the incoming call is
// a bi-directional video call.
- if (CallUtils.isBidirectionalVideoCall(call)) {
+ if (VideoUtils.isBidirectionalVideoCall(call)) {
if (withSms) {
getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_WITH_SMS);
getUi().configureMessageDialog(textMsgs);
diff --git a/InCallUI/src/com/android/incallui/AudioModeProvider.java b/InCallUI/src/com/android/incallui/AudioModeProvider.java
index e7ffecc..961fb11 100644
--- a/InCallUI/src/com/android/incallui/AudioModeProvider.java
+++ b/InCallUI/src/com/android/incallui/AudioModeProvider.java
@@ -16,10 +16,10 @@
package com.android.incallui;
-import android.telecom.CallAudioState;
-
import com.google.common.collect.Lists;
+import com.android.dialer.compat.CallAudioStateCompat;
+
import java.util.List;
/**
@@ -30,19 +30,20 @@
static final int AUDIO_MODE_INVALID = 0;
private static AudioModeProvider sAudioModeProvider = new AudioModeProvider();
- private int mAudioMode = CallAudioState.ROUTE_EARPIECE;
+ private int mAudioMode = CallAudioStateCompat.ROUTE_EARPIECE;
private boolean mMuted = false;
- private int mSupportedModes = CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH |
- CallAudioState.ROUTE_WIRED_HEADSET | CallAudioState.ROUTE_SPEAKER;
+ private int mSupportedModes = CallAudioStateCompat.ROUTE_EARPIECE
+ | CallAudioStateCompat.ROUTE_BLUETOOTH | CallAudioStateCompat.ROUTE_WIRED_HEADSET
+ | CallAudioStateCompat.ROUTE_SPEAKER;
private final List<AudioModeListener> mListeners = Lists.newArrayList();
public static AudioModeProvider getInstance() {
return sAudioModeProvider;
}
- public void onAudioStateChanged(CallAudioState audioState) {
- onAudioModeChange(audioState.getRoute(), audioState.isMuted());
- onSupportedAudioModeChange(audioState.getSupportedRouteMask());
+ public void onAudioStateChanged(boolean isMuted, int route, int supportedRouteMask) {
+ onAudioModeChange(route, isMuted);
+ onSupportedAudioModeChange(supportedRouteMask);
}
public void onAudioModeChange(int newMode, boolean muted) {
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index 7b2724a..9526e7d 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -364,10 +364,12 @@
private int mSessionModificationState;
private final List<String> mChildCallIds = new ArrayList<>();
private final VideoSettings mVideoSettings = new VideoSettings();
+ private int mVideoState;
+
/**
- * mModifyToVideoState is used to store requested upgrade / downgrade video state
+ * mRequestedVideoState is used to store requested upgrade / downgrade video state
*/
- private int mModifyToVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ private int mRequestedVideoState = VideoProfile.STATE_AUDIO_ONLY;
private InCallVideoCallCallback mVideoCallCallback;
private String mChildNumber;
@@ -435,6 +437,7 @@
if (mState != State.BLOCKED) {
setState(translatedState);
setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause());
+ maybeCancelVideoUpgrade(mTelecomCall.getDetails().getVideoState());
}
if (mTelecomCall.getVideoCall() != null) {
@@ -523,6 +526,23 @@
}
}
+ /**
+ * Determines if a received upgrade to video request should be cancelled. This can happen if
+ * another InCall UI responds to the upgrade to video request.
+ *
+ * @param newVideoState The new video state.
+ */
+ private void maybeCancelVideoUpgrade(int newVideoState) {
+ boolean isVideoStateChanged = mVideoState != newVideoState;
+
+ if (mSessionModificationState == SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST
+ && isVideoStateChanged) {
+
+ Log.v(this, "maybeCancelVideoUpgrade : cancelling upgrade notification");
+ setSessionModificationState(SessionModificationState.NO_REQUEST);
+ }
+ mVideoState = newVideoState;
+ }
private static int translateState(int state) {
switch (state) {
case android.telecom.Call.STATE_NEW:
@@ -722,43 +742,43 @@
public boolean isVideoCall(Context context) {
return CallUtil.isVideoEnabled(context) &&
- CallUtils.isVideoCall(getVideoState());
+ VideoUtils.isVideoCall(getVideoState());
}
/**
- * This method is called when we request for a video upgrade or downgrade. This handles the
- * session modification state RECEIVED_UPGRADE_TO_VIDEO_REQUEST and sets the video state we
- * want to upgrade/downgrade to.
+ * Handles incoming session modification requests. Stores the pending video request and sets
+ * the session modification state to
+ * {@link SessionModificationState#RECEIVED_UPGRADE_TO_VIDEO_REQUEST} so that we can keep track
+ * of the fact the request was received. Only upgrade requests require user confirmation and
+ * will be handled by this method. The remote user can turn off their own camera without
+ * confirmation.
+ *
+ * @param videoState The requested video state.
*/
- public void setSessionModificationTo(int videoState) {
- Log.d(this, "setSessionModificationTo - video state= " + videoState);
+ public void setRequestedVideoState(int videoState) {
+ Log.d(this, "setRequestedVideoState - video state= " + videoState);
if (videoState == getVideoState()) {
mSessionModificationState = Call.SessionModificationState.NO_REQUEST;
- Log.w(this,"setSessionModificationTo - Clearing session modification state");
- } else {
- mSessionModificationState =
- Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
- setModifyToVideoState(videoState);
- CallList.getInstance().onUpgradeToVideo(this);
+ Log.w(this,"setRequestedVideoState - Clearing session modification state");
+ return;
}
- Log.d(this, "setSessionModificationTo - mSessionModificationState="
+ mSessionModificationState = Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
+ mRequestedVideoState = videoState;
+ CallList.getInstance().onUpgradeToVideo(this);
+
+ Log.d(this, "setRequestedVideoState - mSessionModificationState="
+ mSessionModificationState + " video state= " + videoState);
update();
}
/**
- * This method is called to handle any other session modification states other than
- * RECEIVED_UPGRADE_TO_VIDEO_REQUEST. We set the modification state and reset the video state
- * when an upgrade request has been completed or failed.
+ * Set the session modification state. Used to keep track of pending video session modification
+ * operations and to inform listeners of these changes.
+ *
+ * @param state the new session modification state.
*/
public void setSessionModificationState(int state) {
- if (state == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
- Log.e(this,
- "setSessionModificationState not valid for RECEIVED_UPGRADE_TO_VIDEO_REQUEST");
- return;
- }
-
boolean hasChanged = mSessionModificationState != state;
mSessionModificationState = state;
Log.d(this, "setSessionModificationState " + state + " mSessionModificationState="
@@ -776,12 +796,13 @@
mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
}
- private void setModifyToVideoState(int newVideoState) {
- mModifyToVideoState = newVideoState;
- }
-
- public int getModifyToVideoState() {
- return mModifyToVideoState;
+ /**
+ * Gets the video state which was requested via a session modification request.
+ *
+ * @return The video state.
+ */
+ public int getRequestedVideoState() {
+ return mRequestedVideoState;
}
public static boolean areSame(Call call1, Call call2) {
@@ -806,6 +827,11 @@
return TextUtils.equals(call1.getNumber(), call2.getNumber());
}
+ /**
+ * Gets the current video session modification state.
+ *
+ * @return The session modification state.
+ */
public int getSessionModificationState() {
return mSessionModificationState;
}
diff --git a/InCallUI/src/com/android/incallui/CallButtonFragment.java b/InCallUI/src/com/android/incallui/CallButtonFragment.java
index 1d32d8f..6538474 100644
--- a/InCallUI/src/com/android/incallui/CallButtonFragment.java
+++ b/InCallUI/src/com/android/incallui/CallButtonFragment.java
@@ -16,18 +16,28 @@
package com.android.incallui;
-import static com.android.incallui.CallButtonFragment.Buttons.*;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_AUDIO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_COUNT;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DIALPAD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_HOLD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MANAGE_VIDEO_CONFERENCE;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MERGE;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MUTE;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Bundle;
-import android.telecom.CallAudioState;
import android.util.SparseIntArray;
import android.view.ContextThemeWrapper;
import android.view.HapticFeedbackConstants;
@@ -43,6 +53,7 @@
import android.widget.PopupMenu.OnMenuItemClickListener;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
+import com.android.dialer.compat.CallAudioStateCompat;
/**
* Fragment for call control buttons
@@ -516,20 +527,20 @@
Log.d(this, " id: " + item.getItemId());
Log.d(this, " title: '" + item.getTitle() + "'");
- int mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
+ int mode = CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE;
switch (item.getItemId()) {
case R.id.audio_mode_speaker:
- mode = CallAudioState.ROUTE_SPEAKER;
+ mode = CallAudioStateCompat.ROUTE_SPEAKER;
break;
case R.id.audio_mode_earpiece:
case R.id.audio_mode_wired_headset:
// InCallCallAudioState.ROUTE_EARPIECE means either the handset earpiece,
// or the wired headset (if connected.)
- mode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
+ mode = CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE;
break;
case R.id.audio_mode_bluetooth:
- mode = CallAudioState.ROUTE_BLUETOOTH;
+ mode = CallAudioStateCompat.ROUTE_BLUETOOTH;
break;
default:
Log.e(this, "onMenuItemClick: unexpected View ID " + item.getItemId()
@@ -559,9 +570,9 @@
*/
private void onAudioButtonClicked() {
Log.d(this, "onAudioButtonClicked: " +
- CallAudioState.audioRouteToString(getPresenter().getSupportedAudio()));
+ CallAudioStateCompat.audioRouteToString(getPresenter().getSupportedAudio()));
- if (isSupported(CallAudioState.ROUTE_BLUETOOTH)) {
+ if (isSupported(CallAudioStateCompat.ROUTE_BLUETOOTH)) {
showAudioModePopup();
} else {
getPresenter().toggleSpeakerphone();
@@ -596,8 +607,8 @@
* are visible based on the supported audio formats.
*/
private void updateAudioButtons(int supportedModes) {
- final boolean bluetoothSupported = isSupported(CallAudioState.ROUTE_BLUETOOTH);
- final boolean speakerSupported = isSupported(CallAudioState.ROUTE_SPEAKER);
+ final boolean bluetoothSupported = isSupported(CallAudioStateCompat.ROUTE_BLUETOOTH);
+ final boolean speakerSupported = isSupported(CallAudioStateCompat.ROUTE_SPEAKER);
boolean audioButtonEnabled = false;
boolean audioButtonChecked = false;
@@ -617,9 +628,9 @@
showMoreIndicator = true;
// Update desired layers:
- if (isAudio(CallAudioState.ROUTE_BLUETOOTH)) {
+ if (isAudio(CallAudioStateCompat.ROUTE_BLUETOOTH)) {
showBluetoothIcon = true;
- } else if (isAudio(CallAudioState.ROUTE_SPEAKER)) {
+ } else if (isAudio(CallAudioStateCompat.ROUTE_SPEAKER)) {
showSpeakerphoneIcon = true;
} else {
showHandsetIcon = true;
@@ -638,7 +649,7 @@
// The audio button *is* a toggle in this state, and indicated the
// current state of the speakerphone.
- audioButtonChecked = isAudio(CallAudioState.ROUTE_SPEAKER);
+ audioButtonChecked = isAudio(CallAudioStateCompat.ROUTE_SPEAKER);
mAudioButton.setSelected(audioButtonChecked);
// update desired layers:
@@ -699,20 +710,20 @@
// If bluetooth is not supported, the audio buttion will toggle, so use the label "speaker".
// Otherwise, use the label of the currently selected audio mode.
- if (!isSupported(CallAudioState.ROUTE_BLUETOOTH)) {
+ if (!isSupported(CallAudioStateCompat.ROUTE_BLUETOOTH)) {
stringId = R.string.audio_mode_speaker;
} else {
switch (mode) {
- case CallAudioState.ROUTE_EARPIECE:
+ case CallAudioStateCompat.ROUTE_EARPIECE:
stringId = R.string.audio_mode_earpiece;
break;
- case CallAudioState.ROUTE_BLUETOOTH:
+ case CallAudioStateCompat.ROUTE_BLUETOOTH:
stringId = R.string.audio_mode_bluetooth;
break;
- case CallAudioState.ROUTE_WIRED_HEADSET:
+ case CallAudioStateCompat.ROUTE_WIRED_HEADSET:
stringId = R.string.audio_mode_wired_headset;
break;
- case CallAudioState.ROUTE_SPEAKER:
+ case CallAudioStateCompat.ROUTE_SPEAKER:
stringId = R.string.audio_mode_speaker;
break;
}
@@ -742,7 +753,7 @@
// See comments below for the exact logic.
final MenuItem speakerItem = menu.findItem(R.id.audio_mode_speaker);
- speakerItem.setEnabled(isSupported(CallAudioState.ROUTE_SPEAKER));
+ speakerItem.setEnabled(isSupported(CallAudioStateCompat.ROUTE_SPEAKER));
// TODO: Show speakerItem as initially "selected" if
// speaker is on.
@@ -751,7 +762,7 @@
final MenuItem earpieceItem = menu.findItem(R.id.audio_mode_earpiece);
final MenuItem wiredHeadsetItem = menu.findItem(R.id.audio_mode_wired_headset);
- final boolean usingHeadset = isSupported(CallAudioState.ROUTE_WIRED_HEADSET);
+ final boolean usingHeadset = isSupported(CallAudioStateCompat.ROUTE_WIRED_HEADSET);
earpieceItem.setVisible(!usingHeadset);
earpieceItem.setEnabled(!usingHeadset);
wiredHeadsetItem.setVisible(usingHeadset);
@@ -761,7 +772,7 @@
// bluetoothIndicatorOn are both false.
final MenuItem bluetoothItem = menu.findItem(R.id.audio_mode_bluetooth);
- bluetoothItem.setEnabled(isSupported(CallAudioState.ROUTE_BLUETOOTH));
+ bluetoothItem.setEnabled(isSupported(CallAudioStateCompat.ROUTE_BLUETOOTH));
// TODO: Show bluetoothItem as initially "selected" if
// bluetoothIndicatorOn is true.
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 29cdd4d..5375b5b 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -16,23 +16,30 @@
package com.android.incallui;
-import static com.android.incallui.CallButtonFragment.Buttons.*;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_ADD_CALL;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_AUDIO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_DIALPAD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_HOLD;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MERGE;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_MUTE;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_PAUSE_VIDEO;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWAP;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_SWITCH_CAMERA;
+import static com.android.incallui.CallButtonFragment.Buttons.BUTTON_UPGRADE_TO_VIDEO;
import android.content.Context;
import android.os.Bundle;
-import android.telecom.CallAudioState;
import android.telecom.InCallService.VideoCall;
import android.telecom.VideoProfile;
+import com.android.dialer.compat.CallAudioStateCompat;
import com.android.incallui.AudioModeProvider.AudioModeListener;
import com.android.incallui.InCallCameraManager.Listener;
import com.android.incallui.InCallPresenter.CanAddCallListener;
+import com.android.incallui.InCallPresenter.InCallDetailsListener;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
import com.android.incallui.InCallPresenter.IncomingCallListener;
-import com.android.incallui.InCallPresenter.InCallDetailsListener;
-
-import java.util.Objects;
/**
* Logic for call buttons.
@@ -177,7 +184,7 @@
// an update for onAudioMode(). This will make UI response immediate
// if it turns out to be slow
- Log.d(this, "Sending new Audio Mode: " + CallAudioState.audioRouteToString(mode));
+ Log.d(this, "Sending new Audio Mode: " + CallAudioStateCompat.audioRouteToString(mode));
TelecomAdapter.getInstance().setAudioRoute(mode);
}
@@ -186,7 +193,7 @@
*/
public void toggleSpeakerphone() {
// this function should not be called if bluetooth is available
- if (0 != (CallAudioState.ROUTE_BLUETOOTH & getSupportedAudio())) {
+ if (0 != (CallAudioStateCompat.ROUTE_BLUETOOTH & getSupportedAudio())) {
// It's clear the UI is wrong, so update the supported mode once again.
Log.e(this, "toggling speakerphone not allowed when bluetooth supported.");
@@ -194,11 +201,11 @@
return;
}
- int newMode = CallAudioState.ROUTE_SPEAKER;
+ int newMode = CallAudioStateCompat.ROUTE_SPEAKER;
// if speakerphone is already on, change to wired/earpiece
- if (getAudioMode() == CallAudioState.ROUTE_SPEAKER) {
- newMode = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
+ if (getAudioMode() == CallAudioStateCompat.ROUTE_SPEAKER) {
+ newMode = CallAudioStateCompat.ROUTE_WIRED_OR_EARPIECE;
}
setAudioMode(newMode);
@@ -266,7 +273,7 @@
return;
}
int currVideoState = mCall.getVideoState();
- int currUnpausedVideoState = CallUtils.getUnPausedVideoState(currVideoState);
+ int currUnpausedVideoState = VideoUtils.getUnPausedVideoState(currVideoState);
currUnpausedVideoState |= VideoProfile.STATE_BIDIRECTIONAL;
VideoProfile videoProfile = new VideoProfile(currUnpausedVideoState);
@@ -356,7 +363,7 @@
Log.v(this, "updateButtonsState");
final CallButtonUi ui = getUi();
- final boolean isVideo = CallUtils.isVideoCall(call);
+ final boolean isVideo = VideoUtils.isVideoCall(call);
// Common functionality (audio, hold, etc).
// Show either HOLD or SWAP, but not both. If neither HOLD or SWAP is available:
@@ -387,7 +394,7 @@
ui.showButton(BUTTON_UPGRADE_TO_VIDEO, showUpgradeToVideo);
ui.showButton(BUTTON_SWITCH_CAMERA, isVideo);
ui.showButton(BUTTON_PAUSE_VIDEO, isVideo);
- ui.showButton(BUTTON_DIALPAD, !isVideo);
+ ui.showButton(BUTTON_DIALPAD, true);
ui.showButton(BUTTON_MERGE, showMerge);
ui.updateButtonStates();
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 3c92a76..dbb2c9d 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -28,13 +28,12 @@
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.os.Trace;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.telecom.DisconnectCause;
-import android.telecom.VideoProfile;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -46,7 +45,6 @@
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageButton;
@@ -57,6 +55,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.contacts.common.widget.FloatingActionButtonController;
import com.android.phone.common.animation.AnimUtils;
@@ -184,6 +183,11 @@
private boolean mCallStateLabelResetPending = false;
private Handler mHandler;
+ /**
+ * Determines if secondary call info is populated in the secondary call info UI.
+ */
+ private boolean mHasSecondaryCallInfo = false;
+
@Override
public CallCardPresenter.CallCardUi getUi() {
return this;
@@ -375,6 +379,7 @@
*/
@Override
public void setCallCardVisible(final boolean visible) {
+ Log.v(this, "setCallCardVisible : isVisible = " + visible);
// When animating the hide/show of the views in a landscape layout, we need to take into
// account whether we are in a left-to-right locale or a right-to-left locale and adjust
// the animations accordingly.
@@ -396,9 +401,7 @@
@Override
public boolean onPreDraw() {
// We don't want to continue getting called.
- if (observer.isAlive()) {
- observer.removeOnPreDrawListener(this);
- }
+ getView().getViewTreeObserver().removeOnPreDrawListener(this);
float videoViewTranslation = 0f;
@@ -407,9 +410,8 @@
mPrimaryCallCardContainer.setTranslationY(visible ?
-mPrimaryCallCardContainer.getHeight() : 0);
- if (visible) {
- videoViewTranslation = videoView.getHeight() / 2 - spaceBesideCallCard / 2;
- }
+ ViewGroup.LayoutParams p = videoView.getLayoutParams();
+ videoViewTranslation = p.height / 2 - spaceBesideCallCard / 2;
}
// Perform animation of video view.
@@ -418,12 +420,10 @@
.setDuration(mVideoAnimationDuration);
if (mIsLandscape) {
videoViewAnimator
- .translationX(videoViewTranslation)
- .start();
+ .translationX(visible ? videoViewTranslation : 0);
} else {
videoViewAnimator
- .translationY(videoViewTranslation)
- .start();
+ .translationY(visible ? videoViewTranslation : 0);
}
videoViewAnimator.start();
@@ -496,7 +496,7 @@
mPrimaryName.setText(null);
} else {
mPrimaryName.setText(nameIsNumber
- ? PhoneNumberUtils.createTtsSpannable(name)
+ ? PhoneNumberUtilsCompat.createTtsSpannable(name)
: name);
// Set direction of the name field
@@ -529,7 +529,7 @@
mPhoneNumber.setText(null);
mPhoneNumber.setVisibility(View.GONE);
} else {
- mPhoneNumber.setText(PhoneNumberUtils.createTtsSpannable(number));
+ mPhoneNumber.setText(PhoneNumberUtilsCompat.createTtsSpannable(number));
mPhoneNumber.setVisibility(View.VISIBLE);
mPhoneNumber.setTextDirection(View.TEXT_DIRECTION_LTR);
}
@@ -586,21 +586,22 @@
@Override
public void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, boolean isConference, boolean isVideoCall) {
-
- if (show != mSecondaryCallInfo.isShown()) {
- updateFabPositionForSecondaryCallInfo();
- }
+ String providerLabel, boolean isConference, boolean isVideoCall, boolean isFullscreen) {
if (show) {
+ mHasSecondaryCallInfo = true;
boolean hasProvider = !TextUtils.isEmpty(providerLabel);
- showAndInitializeSecondaryCallInfo(hasProvider);
+ initializeSecondaryCallInfo(hasProvider);
+
+ // Do not show the secondary caller info in fullscreen mode, but ensure it is populated
+ // in case fullscreen mode is exited in the future.
+ setSecondaryInfoVisible(!isFullscreen);
mSecondaryCallConferenceCallIcon.setVisibility(isConference ? View.VISIBLE : View.GONE);
mSecondaryCallVideoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
mSecondaryCallName.setText(nameIsNumber
- ? PhoneNumberUtils.createTtsSpannable(name)
+ ? PhoneNumberUtilsCompat.createTtsSpannable(name)
: name);
if (hasProvider) {
mSecondaryCallProviderLabel.setText(providerLabel);
@@ -612,10 +613,92 @@
}
mSecondaryCallName.setTextDirection(nameDirection);
} else {
- mSecondaryCallInfo.setVisibility(View.GONE);
+ mHasSecondaryCallInfo = false;
+ setSecondaryInfoVisible(false);
}
}
+ /**
+ * Sets the visibility of the secondary caller info box. Note, if the {@code visible} parameter
+ * is passed in {@code true}, and there is no secondary caller info populated (as determined by
+ * {@code mHasSecondaryCallInfo}, the secondary caller info box will not be shown.
+ *
+ * @param visible {@code true} if the secondary caller info should be shown, {@code false}
+ * otherwise.
+ */
+ @Override
+ public void setSecondaryInfoVisible(final boolean visible) {
+ boolean wasVisible = mSecondaryCallInfo.isShown();
+ final boolean isVisible = visible && mHasSecondaryCallInfo;
+ Log.v(this, "setSecondaryInfoVisible: wasVisible = " + wasVisible + " isVisible = "
+ + isVisible);
+
+ // If visibility didn't change, nothing to do.
+ if (wasVisible == isVisible) {
+ return;
+ }
+
+ // If we are showing the secondary info, we need to show it before animating so that its
+ // height will be determined on layout.
+ if (isVisible) {
+ mSecondaryCallInfo.setVisibility(View.VISIBLE);
+ }
+
+ updateFabPositionForSecondaryCallInfo();
+ // We need to translate the secondary caller info, but we need to know its position after
+ // the layout has occurred so use a {@code ViewTreeObserver}.
+ final ViewTreeObserver observer = getView().getViewTreeObserver();
+
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // We don't want to continue getting called.
+ getView().getViewTreeObserver().removeOnPreDrawListener(this);
+
+ // Get the height of the secondary call info now, and then re-hide the view prior
+ // to doing the actual animation.
+ int secondaryHeight = mSecondaryCallInfo.getHeight();
+ if (isVisible) {
+ mSecondaryCallInfo.setVisibility(View.GONE);
+ }
+ Log.v(this, "setSecondaryInfoVisible: secondaryHeight = " + secondaryHeight);
+
+ // Set the position of the secondary call info card to its starting location.
+ mSecondaryCallInfo.setTranslationY(visible ? secondaryHeight : 0);
+
+ // Animate the secondary card info slide up/down as it appears and disappears.
+ ViewPropertyAnimator secondaryInfoAnimator = mSecondaryCallInfo.animate()
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mVideoAnimationDuration)
+ .translationY(isVisible ? 0 : secondaryHeight)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!isVisible) {
+ mSecondaryCallInfo.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (isVisible) {
+ mSecondaryCallInfo.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ secondaryInfoAnimator.start();
+
+ // Notify listeners of a change in the visibility of the secondary info. This is
+ // important when in a video call so that the video call presenter can shift the
+ // video preview up or down to accommodate the secondary caller info.
+ InCallPresenter.getInstance().notifySecondaryCallerInfoVisibilityChanged(visible,
+ secondaryHeight);
+
+ return true;
+ }
+ });
+ }
+
@Override
public void setCallState(
int state,
@@ -701,7 +784,7 @@
mCallStateIcon.setVisibility(View.GONE);
}
- if (CallUtils.isVideoCall(videoState)
+ if (VideoUtils.isVideoCall(videoState)
|| (state == Call.State.ACTIVE && sessionModificationState
== Call.SessionModificationState.WAITING_FOR_RESPONSE)) {
mCallStateVideoCallIcon.setVisibility(View.VISIBLE);
@@ -953,7 +1036,7 @@
} 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)) {
+ } else if (VideoUtils.isVideoCall(videoState)) {
callStateLabel = context.getString(R.string.card_title_video_call);
}
break;
@@ -977,7 +1060,7 @@
callStateLabel = label;
} else if (isAccount) {
callStateLabel = context.getString(R.string.incoming_via_template, label);
- } else if (CallUtils.isVideoCall(videoState)) {
+ } else if (VideoUtils.isVideoCall(videoState)) {
callStateLabel = context.getString(R.string.notification_incoming_video_call);
} else {
callStateLabel = context.getString(R.string.card_title_incoming_call);
@@ -1007,9 +1090,7 @@
return new CallStateLabel(callStateLabel, isAutoDismissing);
}
- private void showAndInitializeSecondaryCallInfo(boolean hasProvider) {
- mSecondaryCallInfo.setVisibility(View.VISIBLE);
-
+ private void initializeSecondaryCallInfo(boolean hasProvider) {
// mSecondaryCallName is initialized here (vs. onViewCreated) because it is inaccessible
// until mSecondaryCallInfo is inflated in the call above.
if (mSecondaryCallName == null) {
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index ba5823d..170e785 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -83,6 +83,7 @@
private boolean mSpinnerShowing = false;
private boolean mHasShownToast = false;
private InCallContactInteractions mInCallContactInteractions;
+ private boolean mIsFullscreen = false;
public static class ContactLookupCallback implements ContactInfoCacheCallback {
private final WeakReference<CallCardPresenter> mCallCardPresenter;
@@ -775,7 +776,7 @@
if (mSecondary == null) {
// Clear the secondary display info.
ui.setSecondary(false, null, false, null, null, false /* isConference */,
- false /* isVideoCall */);
+ false /* isVideoCall */, mIsFullscreen);
return;
}
@@ -787,7 +788,8 @@
null /* label */,
getCallProviderLabel(mSecondary),
true /* isConference */,
- mSecondary.isVideoCall(mContext));
+ mSecondary.isVideoCall(mContext),
+ mIsFullscreen);
} else if (mSecondaryContactInfo != null) {
Log.d(TAG, "updateSecondaryDisplayInfo() " + mSecondaryContactInfo);
String name = getNameForCall(mSecondaryContactInfo);
@@ -799,11 +801,12 @@
mSecondaryContactInfo.label,
getCallProviderLabel(mSecondary),
false /* isConference */,
- mSecondary.isVideoCall(mContext));
+ mSecondary.isVideoCall(mContext),
+ mIsFullscreen);
} else {
// Clear the secondary display info.
ui.setSecondary(false, null, false, null, null, false /* isConference */,
- false /* isVideoCall */);
+ false /* isVideoCall */, mIsFullscreen);
}
}
@@ -955,11 +958,18 @@
*/
@Override
public void onFullscreenModeChanged(boolean isFullscreenMode) {
+ mIsFullscreen = isFullscreenMode;
final CallCardUi ui = getUi();
if (ui == null) {
return;
}
ui.setCallCardVisible(!isFullscreenMode);
+ ui.setSecondaryInfoVisible(!isFullscreenMode);
+ }
+
+ @Override
+ public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
+ // No-op - the Call Card is the origin of this event.
}
private boolean isPrimaryCallActive() {
@@ -1061,7 +1071,9 @@
void setPrimary(String number, String name, boolean nameIsNumber, String label,
Drawable photo, boolean isSipCall, boolean isContactPhotoShown);
void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
- String providerLabel, boolean isConference, boolean isVideoCall);
+ String providerLabel, boolean isConference, boolean isVideoCall,
+ boolean isFullscreen);
+ void setSecondaryInfoVisible(boolean visible);
void setCallState(int state, int videoState, int sessionModificationState,
DisconnectCause disconnectCause, String connectionLabel,
Drawable connectionIcon, String gatewayNumber, boolean isWifi,
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index 37ae14b..0f31f72 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -569,9 +569,10 @@
// Second, 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).
+
// NOTE: is it necessary to use this order because getVideoCall references the class
// VideoProfile which is not available on APIs <23 (M).
- if (CallUtils.isVideoCall(call) && call.getVideoCall() != null) {
+ if (VideoUtils.isVideoCall(call) && call.getVideoCall() != null) {
call.getVideoCall().setDeviceOrientation(rotation);
}
}
diff --git a/InCallUI/src/com/android/incallui/CallUtils.java b/InCallUI/src/com/android/incallui/CallUtils.java
deleted file mode 100644
index 6eb1a05..0000000
--- a/InCallUI/src/com/android/incallui/CallUtils.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- * * Neither the name of The Linux Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-package com.android.incallui;
-
-import android.telecom.VideoProfile;
-
-import com.android.dialer.compat.DialerCompatUtils;
-
-import com.google.common.base.Preconditions;
-
-public class CallUtils {
-
- public static boolean isVideoCall(Call call) {
- return call != null && isVideoCall(call.getVideoState());
- }
-
- public static boolean isVideoCall(int videoState) {
- if (!DialerCompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isTransmissionEnabled(videoState)
- || VideoProfile.isReceptionEnabled(videoState);
- }
-
- public static boolean isBidirectionalVideoCall(Call call) {
- if (!DialerCompatUtils.isVideoCompatible()) {
- return false;
- }
-
- return VideoProfile.isBidirectional(call.getVideoState());
- }
-
- public static boolean isIncomingVideoCall(Call call) {
- if (!CallUtils.isVideoCall(call)) {
- return false;
- }
- final int state = call.getState();
- return (state == Call.State.INCOMING) || (state == Call.State.CALL_WAITING);
- }
-
- public static boolean isActiveVideoCall(Call call) {
- return CallUtils.isVideoCall(call) && call.getState() == Call.State.ACTIVE;
- }
-
- public static boolean isOutgoingVideoCall(Call call) {
- if (!CallUtils.isVideoCall(call)) {
- return false;
- }
- final int state = call.getState();
- return Call.State.isDialing(state) || state == Call.State.CONNECTING
- || state == Call.State.SELECT_PHONE_ACCOUNT;
- }
-
- public static boolean isAudioCall(Call call) {
- if (!DialerCompatUtils.isVideoCompatible()) {
- return true;
- }
-
- return call != null && VideoProfile.isAudioOnly(call.getVideoState());
- }
-
- // TODO (ims-vt) Check if special handling is needed for CONF calls.
- public static boolean canVideoPause(Call call) {
- return isVideoCall(call) && call.getState() == Call.State.ACTIVE;
- }
-
- public static VideoProfile makeVideoPauseProfile(Call call) {
- Preconditions.checkNotNull(call);
- Preconditions.checkState(!VideoProfile.isAudioOnly(call.getVideoState()));
- return new VideoProfile(getPausedVideoState(call.getVideoState()));
- }
-
- public static VideoProfile makeVideoUnPauseProfile(Call call) {
- Preconditions.checkNotNull(call);
- return new VideoProfile(getUnPausedVideoState(call.getVideoState()));
- }
-
- public static int getUnPausedVideoState(int videoState) {
- return videoState & (~VideoProfile.STATE_PAUSED);
- }
-
- public static int getPausedVideoState(int videoState) {
- return videoState | VideoProfile.STATE_PAUSED;
- }
-
-}
diff --git a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
index a595c43..ad8de74 100644
--- a/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
+++ b/InCallUI/src/com/android/incallui/ConferenceParticipantListAdapter.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.net.Uri;
-import android.telephony.PhoneNumberUtils;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
@@ -34,6 +33,7 @@
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.common.util.ContactDisplayUtils;
import com.android.incallui.ContactInfoCache.ContactCacheEntry;
@@ -438,7 +438,7 @@
numberTypeTextView.setVisibility(View.GONE);
} else {
numberTextView.setVisibility(View.VISIBLE);
- numberTextView.setText(PhoneNumberUtils.createTtsSpannable(
+ numberTextView.setText(PhoneNumberUtilsCompat.createTtsSpannable(
BidiFormatter.getInstance().unicodeWrap(
callerNumber, TextDirectionHeuristics.LTR)));
numberTypeTextView.setVisibility(View.VISIBLE);
diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java
index 371f5c5..ab44cf2 100644
--- a/InCallUI/src/com/android/incallui/DialpadFragment.java
+++ b/InCallUI/src/com/android/incallui/DialpadFragment.java
@@ -20,7 +20,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.telephony.PhoneNumberUtils;
import android.text.Editable;
import android.text.method.DialerKeyListener;
import android.util.AttributeSet;
@@ -34,6 +33,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.phone.common.dialpad.DialpadKeyButton;
import com.android.phone.common.dialpad.DialpadView;
@@ -483,7 +483,7 @@
* @param text Text to set Dialpad EditText to.
*/
public void setDtmfText(String text) {
- mDtmfDialerField.setText(PhoneNumberUtils.createTtsSpannable(text));
+ mDtmfDialerField.setText(PhoneNumberUtilsCompat.createTtsSpannable(text));
}
@Override
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index cbcf50c..2eab7d3 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -82,6 +82,10 @@
private static final String TAG_ANSWER_FRAGMENT = "tag_answer_fragment";
private static final String TAG_SELECT_ACCT_FRAGMENT = "tag_select_acct_fragment";
+ private static final int DIALPAD_REQUEST_NONE = 1;
+ private static final int DIALPAD_REQUEST_SHOW = 2;
+ private static final int DIALPAD_REQUEST_HIDE = 3;
+
private CallButtonFragment mCallButtonFragment;
private CallCardFragment mCallCardFragment;
private AnswerFragment mAnswerFragment;
@@ -90,11 +94,15 @@
private FragmentManager mChildFragmentManager;
private AlertDialog mDialog;
+ private InCallOrientationEventListener mInCallOrientationEventListener;
/**
- * Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume}
+ * Used to indicate whether the dialpad should be hidden or shown {@link #onResume}.
+ * {@code #DIALPAD_REQUEST_SHOW} indicates that the dialpad should be shown.
+ * {@code #DIALPAD_REQUEST_HIDE} indicates that the dialpad should be hidden.
+ * {@code #DIALPAD_REQUEST_NONE} indicates no change should be made to dialpad visibility.
*/
- private boolean mShowDialpadRequested;
+ private int mShowDialpadRequest = DIALPAD_REQUEST_NONE;
/**
* Use to determine if the dialpad should be animated on show.
@@ -141,16 +149,6 @@
}
};
- /**
- * Listener for orientation changes.
- */
- private OrientationEventListener mOrientationEventListener;
-
- /**
- * Used to determine if a change in rotation has occurred.
- */
- private static int sPreviousRotation = -1;
-
@Override
protected void onCreate(Bundle icicle) {
Log.d(this, "onCreate()... this = " + this);
@@ -202,13 +200,24 @@
mSlideOut.setAnimationListener(mSlideOutListener);
+ // If the dialpad fragment already exists, retrieve it. This is important when rotating as
+ // we will not be able to hide or show the dialpad after the rotation otherwise.
+ Fragment existingFragment =
+ getFragmentManager().findFragmentByTag(DialpadFragment.class.getName());
+ if (existingFragment != null) {
+ mDialpadFragment = (DialpadFragment) existingFragment;
+ }
+
if (icicle != null) {
// If the dialpad was shown before, set variables indicating it should be shown and
// populated with the previous DTMF text. The dialpad is actually shown and populated
// in onResume() to ensure the hosting CallCardFragment has been inflated and is ready
// to receive it.
- mShowDialpadRequested = icicle.getBoolean(SHOW_DIALPAD_EXTRA);
- mAnimateDialpadOnShow = false;
+ if (icicle.containsKey(SHOW_DIALPAD_EXTRA)) {
+ boolean showDialpad = icicle.getBoolean(SHOW_DIALPAD_EXTRA);
+ mShowDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
+ mAnimateDialpadOnShow = false;
+ }
mDtmfText = icicle.getString(DIALPAD_TEXT_EXTRA);
SelectPhoneAccountDialogFragment dialogFragment = (SelectPhoneAccountDialogFragment)
@@ -217,39 +226,7 @@
dialogFragment.setListener(mSelectAcctListener);
}
}
-
- mOrientationEventListener = new OrientationEventListener(this,
- SensorManager.SENSOR_DELAY_NORMAL) {
- @Override
- public void onOrientationChanged(int orientation) {
- // Device is flat, don't change orientation.
- if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
- return;
- }
-
- int newRotation = Surface.ROTATION_0;
- // We only shift if we're within 22.5 (23) degrees of the target
- // orientation. This avoids flopping back and forth when holding
- // the device at 45 degrees or so.
- if (orientation >= 337 || orientation <= 23) {
- newRotation = Surface.ROTATION_0;
- } else if (orientation >= 67 && orientation <= 113) {
- // Why not 90? Because screen and sensor orientation are
- // reversed.
- newRotation = Surface.ROTATION_270;
- } else if (orientation >= 157 && orientation <= 203) {
- newRotation = Surface.ROTATION_180;
- } else if (orientation >= 247 && orientation <= 293) {
- newRotation = Surface.ROTATION_90;
- }
-
- // Orientation is the current device orientation in degrees. Ultimately we want
- // the rotation (in fixed 90 degree intervals).
- if (newRotation != sPreviousRotation) {
- doOrientationChanged(newRotation);
- }
- }
- };
+ mInCallOrientationEventListener = new InCallOrientationEventListener(this);
Log.d(this, "onCreate(): exit");
}
@@ -270,16 +247,10 @@
Log.d(this, "onStart()...");
super.onStart();
- if (mOrientationEventListener.canDetectOrientation()) {
- Log.v(this, "Orientation detection enabled.");
- mOrientationEventListener.enable();
- } else {
- Log.v(this, "Orientation detection disabled.");
- mOrientationEventListener.disable();
- }
-
// setting activity should be last thing in setup process
InCallPresenter.getInstance().setActivity(this);
+ enableInCallOrientationEventListener(getRequestedOrientation() ==
+ InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
InCallPresenter.getInstance().onActivityStarted();
}
@@ -292,16 +263,31 @@
InCallPresenter.getInstance().setThemeColors();
InCallPresenter.getInstance().onUiShowing(true);
- if (mShowDialpadRequested) {
- mCallButtonFragment.displayDialpad(true /* show */,
- mAnimateDialpadOnShow /* animate */);
- mShowDialpadRequested = false;
- mAnimateDialpadOnShow = false;
+ // Clear fullscreen state onResume; the stored value may not match reality.
+ InCallPresenter.getInstance().clearFullscreen();
- if (mDialpadFragment != null) {
- mDialpadFragment.setDtmfText(mDtmfText);
- mDtmfText = null;
+ // If there is a pending request to show or hide the dialpad, handle that now.
+ if (mShowDialpadRequest != DIALPAD_REQUEST_NONE) {
+ if (mShowDialpadRequest == DIALPAD_REQUEST_SHOW) {
+ // Exit fullscreen so that the user has access to the dialpad hide/show button and
+ // can hide the dialpad. Important when showing the dialpad from within dialer.
+ InCallPresenter.getInstance().setFullScreen(false, true /* force */);
+
+ mCallButtonFragment.displayDialpad(true /* show */,
+ mAnimateDialpadOnShow /* animate */);
+ mAnimateDialpadOnShow = false;
+
+ if (mDialpadFragment != null) {
+ mDialpadFragment.setDtmfText(mDtmfText);
+ mDtmfText = null;
+ }
+ } else {
+ Log.v(this, "onResume : force hide dialpad");
+ if (mDialpadFragment != null) {
+ mCallButtonFragment.displayDialpad(false /* show */, false /* animate */);
+ }
}
+ mShowDialpadRequest = DIALPAD_REQUEST_NONE;
}
if (mShowPostCharWaitDialogOnResume) {
@@ -328,9 +314,9 @@
@Override
protected void onStop() {
Log.d(this, "onStop()...");
+ enableInCallOrientationEventListener(false);
InCallPresenter.getInstance().updateIsChangingConfigurations();
InCallPresenter.getInstance().onActivityStopped();
- mOrientationEventListener.disable();
super.onStop();
}
@@ -544,25 +530,6 @@
return false;
}
- /**
- * Handles changes in device rotation.
- *
- * @param rotation The new device rotation (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
- */
- 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 (rotation != sPreviousRotation) {
- sPreviousRotation = rotation;
- InCallPresenter.getInstance().onDeviceRotationChange(rotation);
- InCallPresenter.getInstance().onDeviceOrientationChange(sPreviousRotation);
- }
- }
-
public CallButtonFragment getCallButtonFragment() {
return mCallButtonFragment;
}
@@ -657,16 +624,23 @@
} else if (!newOutgoingCall) {
showCallCardFragment(true);
}
-
return;
}
}
+ /**
+ * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad
+ * should be shown on launch.
+ *
+ * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and
+ * {@code false} to indicate no change should be made to the
+ * dialpad visibility.
+ */
private void relaunchedFromDialer(boolean showDialpad) {
- mShowDialpadRequested = showDialpad;
+ mShowDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
mAnimateDialpadOnShow = true;
- if (mShowDialpadRequested) {
+ if (mShowDialpadRequest == DIALPAD_REQUEST_SHOW) {
// If there's only one line in use, AND it's on hold, then we're sure the user
// wants to use the dialpad toward the exact line, so un-hold the holding line.
final Call call = CallList.getInstance().getActiveOrBackgroundCall();
@@ -941,4 +915,17 @@
public void setDispatchTouchEventListener(OnTouchListener mDispatchTouchEventListener) {
this.mDispatchTouchEventListener = mDispatchTouchEventListener;
}
+
+ /**
+ * Enables the OrientationEventListener if enable flag is true. Disables it if enable is
+ * false
+ * @param enable true or false.
+ */
+ public void enableInCallOrientationEventListener(boolean enable) {
+ if (enable) {
+ mInCallOrientationEventListener.enable(enable);
+ } else {
+ mInCallOrientationEventListener.disable();
+ }
+ }
}
diff --git a/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java b/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
new file mode 100644
index 0000000..d3334a3
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallOrientationEventListener.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.OrientationEventListener;
+import android.hardware.SensorManager;
+import android.view.Surface;
+import android.content.pm.ActivityInfo;
+
+/**
+ * This class listens to Orientation events and overrides onOrientationChanged which gets
+ * invoked when an orientation change occurs. When that happens, we notify InCallUI registrants
+ * of the change.
+ */
+public class InCallOrientationEventListener extends OrientationEventListener {
+
+ /**
+ * Screen orientation angles one of 0, 90, 180, 270, 360 in degrees.
+ */
+ public static int SCREEN_ORIENTATION_0 = 0;
+ public static int SCREEN_ORIENTATION_90 = 90;
+ public static int SCREEN_ORIENTATION_180 = 180;
+ public static int SCREEN_ORIENTATION_270 = 270;
+ public static int SCREEN_ORIENTATION_360 = 360;
+
+ public static int FULL_SENSOR_SCREEN_ORIENTATION =
+ ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
+
+ public static int NO_SENSOR_SCREEN_ORIENTATION =
+ ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+
+ /**
+ * This is to identify dead zones where we won't notify others of orientation changed.
+ * Say for e.g our threshold is x degrees. We will only notify UI when our current rotation is
+ * within x degrees right or left of the screen orientation angles. If it's not within those
+ * ranges, we return SCREEN_ORIENTATION_UNKNOWN and ignore it.
+ */
+ private static int SCREEN_ORIENTATION_UNKNOWN = -1;
+
+ // Rotation threshold is 10 degrees. So if the rotation angle is within 10 degrees of any of
+ // the above angles, we will notify orientation changed.
+ private static int ROTATION_THRESHOLD = 10;
+
+
+ /**
+ * Cache the current rotation of the device.
+ */
+ private static int sCurrentOrientation = SCREEN_ORIENTATION_0;
+
+ public InCallOrientationEventListener(Context context) {
+ super(context);
+ }
+
+ /**
+ * Handles changes in device orientation. Notifies InCallPresenter of orientation changes.
+ *
+ * Note that this API receives sensor rotation in degrees as a param and we convert that to
+ * one of our screen orientation constants - (one of: {@link SCREEN_ORIENTATION_0},
+ * {@link SCREEN_ORIENTATION_90}, {@link SCREEN_ORIENTATION_180},
+ * {@link SCREEN_ORIENTATION_270}).
+ *
+ * @param rotation The new device sensor rotation in degrees
+ */
+ @Override
+ public void onOrientationChanged(int rotation) {
+ if (rotation == OrientationEventListener.ORIENTATION_UNKNOWN) {
+ return;
+ }
+
+ final int orientation = toScreenOrientation(rotation);
+
+ if (orientation != SCREEN_ORIENTATION_UNKNOWN && sCurrentOrientation != orientation) {
+ sCurrentOrientation = orientation;
+ InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
+ }
+ }
+
+ /**
+ * Enables the OrientationEventListener and notifies listeners of current orientation if
+ * notify flag is true
+ * @param notify true or false. Notify device orientation changed if true.
+ */
+ public void enable(boolean notify) {
+ super.enable();
+ if (notify) {
+ InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
+ }
+ }
+
+ /**
+ * Enables the OrientationEventListener with notify flag defaulting to false.
+ */
+ public void enable() {
+ enable(false);
+ }
+
+ /**
+ * Converts sensor rotation in degrees to screen orientation constants.
+ * @param rotation sensor rotation angle in degrees
+ * @return Screen orientation angle in degrees (0, 90, 180, 270). Returns -1 for degrees not
+ * within threshold to identify zones where orientation change should not be trigerred.
+ */
+ private int toScreenOrientation(int rotation) {
+ // Sensor orientation 90 is equivalent to screen orientation 270 and vice versa. This
+ // function returns the screen orientation. So we convert sensor rotation 90 to 270 and
+ // vice versa here.
+ if (isInLeftRange(rotation, SCREEN_ORIENTATION_360, ROTATION_THRESHOLD) ||
+ isInRightRange(rotation, SCREEN_ORIENTATION_0, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_0;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_90, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_270;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_180, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_180;
+ } else if (isWithinThreshold(rotation, SCREEN_ORIENTATION_270, ROTATION_THRESHOLD)) {
+ return SCREEN_ORIENTATION_90;
+ }
+ return SCREEN_ORIENTATION_UNKNOWN;
+ }
+
+ private static boolean isWithinRange(int value, int begin, int end) {
+ return value >= begin && value < end;
+ }
+
+ private static boolean isWithinThreshold(int value, int center, int threshold) {
+ return isWithinRange(value, center - threshold, center + threshold);
+ }
+
+ private static boolean isInLeftRange(int value, int center, int threshold) {
+ return isWithinRange(value, center - threshold, center);
+ }
+
+ private static boolean isInRightRange(int value, int center, int threshold) {
+ return isWithinRange(value, center, center + threshold);
+ }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 33e4e5a..7d6409c 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -939,17 +939,26 @@
return mIsActivityPreviouslyStarted;
}
+ /**
+ * Determines if the In-Call app is currently changing configuration.
+ *
+ * @return {@code true} if the In-Call app is changing configuration.
+ */
public boolean isChangingConfigurations() {
return mIsChangingConfigurations;
}
+ /**
+ * Tracks whether the In-Call app is currently in the process of changing configuration (i.e.
+ * screen orientation).
+ */
/*package*/
void updateIsChangingConfigurations() {
mIsChangingConfigurations = false;
if (mInCallActivity != null) {
mIsChangingConfigurations = mInCallActivity.isChangingConfigurations();
}
- Log.d(this, "IsChangingConfigurations=" + mIsChangingConfigurations);
+ Log.v(this, "updateIsChangingConfigurations = " + mIsChangingConfigurations);
}
@@ -1132,21 +1141,46 @@
* @return {@code true} if in-call is now in fullscreen mode.
*/
public boolean toggleFullscreenMode() {
- mIsFullScreen = !mIsFullScreen;
- Log.v(this, "toggleFullscreenMode = " + mIsFullScreen);
- notifyFullscreenModeChange(mIsFullScreen);
+ boolean isFullScreen = !mIsFullScreen;
+ Log.v(this, "toggleFullscreenMode = " + isFullScreen);
+ setFullScreen(isFullScreen);
return mIsFullScreen;
}
/**
+ * Clears the previous fullscreen state.
+ */
+ public void clearFullscreen() {
+ mIsFullScreen = false;
+ }
+
+ /**
* Changes the fullscreen mode of the in-call UI.
*
* @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
* otherwise.
*/
public void setFullScreen(boolean isFullScreen) {
+ setFullScreen(isFullScreen, false /* force */);
+ }
+
+ /**
+ * Changes the fullscreen mode of the in-call UI.
+ *
+ * @param isFullScreen {@code true} if in-call should be in fullscreen mode, {@code false}
+ * otherwise.
+ * @param force {@code true} if fullscreen mode should be set regardless of its current state.
+ */
+ public void setFullScreen(boolean isFullScreen, boolean force) {
Log.v(this, "setFullScreen = " + isFullScreen);
- if (mIsFullScreen == isFullScreen) {
+
+ // As a safeguard, ensure we cannot enter fullscreen if the dialpad is shown.
+ if (isDialpadVisible()) {
+ isFullScreen = false;
+ Log.v(this, "setFullScreen overridden as dialpad is shown = " + isFullScreen);
+ }
+
+ if (mIsFullScreen == isFullScreen && !force) {
Log.v(this, "setFullScreen ignored as already in that state.");
return;
}
@@ -1175,6 +1209,21 @@
}
/**
+ * Called by the {@link CallCardPresenter} to inform of a change in visibility of the secondary
+ * caller info bar.
+ *
+ * @param isVisible {@code true} if the secondary caller info is visible, {@code false}
+ * otherwise.
+ * @param height the height of the secondary caller info bar.
+ */
+ public void notifySecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
+ for (InCallEventListener listener : mInCallEventListeners) {
+ listener.onSecondaryCallerInfoVisibilityChanged(isVisible, height);
+ }
+ }
+
+
+ /**
* For some disconnected causes, we show a dialog. This calls into the activity to show
* the dialog if appropriate for the call.
*/
@@ -1510,64 +1559,32 @@
}
/**
- * Handles changes to the device rotation.
+ * Notifies listeners of changes in orientation and notify calls of rotation angle change.
*
- * @param rotation The device rotation (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
- */
- public void onDeviceRotationChange(int rotation) {
- Log.d(this, "onDeviceRotationChange: rotation=" + rotation);
- // First translate to rotation in degrees.
- if (mCallList != null) {
- mCallList.notifyCallsOfDeviceRotation(toRotationAngle(rotation));
- } else {
- Log.w(this, "onDeviceRotationChange: CallList is null.");
- }
- }
-
- /**
- * Converts rotation constants to rotation in degrees.
- * @param rotation Rotation constants (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
- */
- public static int toRotationAngle(int rotation) {
- int rotationAngle;
- switch (rotation) {
- case Surface.ROTATION_0:
- rotationAngle = 0;
- break;
- case Surface.ROTATION_90:
- rotationAngle = 90;
- break;
- case Surface.ROTATION_180:
- rotationAngle = 180;
- break;
- case Surface.ROTATION_270:
- rotationAngle = 270;
- break;
- default:
- rotationAngle = 0;
- }
- return rotationAngle;
- }
-
- /**
- * Notifies listeners of changes in orientation (e.g. portrait/landscape).
- *
- * @param orientation The orientation of the device (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
+ * @param orientation The screen orientation of the device (one of:
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
*/
public void onDeviceOrientationChange(int orientation) {
+ Log.d(this, "onDeviceOrientationChange: orientation= " + orientation);
+
+ if (mCallList != null) {
+ mCallList.notifyCallsOfDeviceRotation(orientation);
+ } else {
+ Log.w(this, "onDeviceOrientationChange: CallList is null.");
+ }
+
+ // Notify listeners of device orientation changed.
for (InCallOrientationListener listener : mOrientationListeners) {
listener.onDeviceOrientationChanged(orientation);
}
}
/**
- * Configures the in-call UI activity so it can change orientations or not.
+ * Configures the in-call UI activity so it can change orientations or not. Enables the
+ * orientation event listener if allowOrientationChange is true, disables it if false.
*
* @param allowOrientationChange {@code True} if the in-call UI can change between portrait
* and landscape. {@Code False} if the in-call UI should be locked in portrait.
@@ -1579,10 +1596,15 @@
}
if (!allowOrientationChange) {
- mInCallActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
+ mInCallActivity.setRequestedOrientation(
+ InCallOrientationEventListener.NO_SENSOR_SCREEN_ORIENTATION);
} else {
- mInCallActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+ // Using SCREEN_ORIENTATION_FULL_SENSOR allows for reverse-portrait orientation, where
+ // SCREEN_ORIENTATION_SENSOR does not.
+ mInCallActivity.setRequestedOrientation(
+ InCallOrientationEventListener.FULL_SENSOR_SCREEN_ORIENTATION);
}
+ mInCallActivity.enableInCallOrientationEventListener(allowOrientationChange);
}
public void enableScreenTimeout(boolean enable) {
@@ -1639,6 +1661,18 @@
}
/**
+ * Determines if the dialpad is visible.
+ *
+ * @return {@code true} if the dialpad is visible, {@code false} otherwise.
+ */
+ public boolean isDialpadVisible() {
+ if (mInCallActivity == null) {
+ return false;
+ }
+ return mInCallActivity.isDialpadVisible();
+ }
+
+ /**
* @return True if the application is currently running in a right-to-left locale.
*/
public static boolean isRtl() {
@@ -1796,6 +1830,7 @@
*/
public interface InCallEventListener {
public void onFullscreenModeChanged(boolean isFullscreenMode);
+ public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height);
}
public interface InCallUiListener {
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index 11202e7..3e4f714 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -23,6 +23,7 @@
import android.telecom.CallAudioState;
import android.telecom.InCallService;
+import com.android.dialer.compat.CallAudioStateCompat;
import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
/**
@@ -33,9 +34,11 @@
*/
public class InCallServiceImpl extends InCallService {
+ // TODO CallAudioState backporting blocked by InCallService backporting
@Override
public void onCallAudioStateChanged(CallAudioState audioState) {
- AudioModeProvider.getInstance().onAudioStateChanged(audioState);
+ AudioModeProvider.getInstance().onAudioStateChanged(audioState.isMuted(),
+ audioState.getRoute(), audioState.getSupportedRouteMask());
}
@Override
diff --git a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
index 87b886d..76f8c09 100644
--- a/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
+++ b/InCallUI/src/com/android/incallui/InCallVideoCallCallback.java
@@ -49,11 +49,11 @@
@Override
public void onSessionModifyRequestReceived(VideoProfile videoProfile) {
Log.d(this, " onSessionModifyRequestReceived videoProfile=" + videoProfile);
- int previousVideoState = CallUtils.getUnPausedVideoState(mCall.getVideoState());
- int newVideoState = CallUtils.getUnPausedVideoState(videoProfile.getVideoState());
+ int previousVideoState = VideoUtils.getUnPausedVideoState(mCall.getVideoState());
+ int newVideoState = VideoUtils.getUnPausedVideoState(videoProfile.getVideoState());
- boolean wasVideoCall = CallUtils.isVideoCall(previousVideoState);
- boolean isVideoCall = CallUtils.isVideoCall(newVideoState);
+ boolean wasVideoCall = VideoUtils.isVideoCall(previousVideoState);
+ boolean isVideoCall = VideoUtils.isVideoCall(newVideoState);
// Check for upgrades to video and downgrades to audio.
if (wasVideoCall && !isVideoCall) {
@@ -97,7 +97,7 @@
} else if (requestedProfile != null && responseProfile != null) {
boolean modifySucceeded = requestedProfile.getVideoState() ==
responseProfile.getVideoState();
- boolean isVideoCall = CallUtils.isVideoCall(responseProfile.getVideoState());
+ boolean isVideoCall = VideoUtils.isVideoCall(responseProfile.getVideoState());
if (modifySucceeded && isVideoCall) {
InCallVideoCallCallbackNotifier.getInstance().upgradeToVideoSuccess(mCall);
} else if (!modifySucceeded && isVideoCall) {
diff --git a/InCallUI/src/com/android/incallui/ProximitySensor.java b/InCallUI/src/com/android/incallui/ProximitySensor.java
index 401ebd1..733a67d 100644
--- a/InCallUI/src/com/android/incallui/ProximitySensor.java
+++ b/InCallUI/src/com/android/incallui/ProximitySensor.java
@@ -16,20 +16,20 @@
package com.android.incallui;
+import com.google.common.base.Objects;
+
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.os.PowerManager;
-import android.telecom.CallAudioState;
import android.view.Display;
+import com.android.dialer.compat.CallAudioStateCompat;
import com.android.incallui.AudioModeProvider.AudioModeListener;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
-import com.google.common.base.Objects;
-
/**
* Class manages the proximity sensor for the in-call UI.
* We enable the proximity sensor while the user in a phone call. The Proximity sensor turns off
@@ -228,9 +228,9 @@
// turn proximity sensor off and turn screen on immediately if
// we are using a headset, the keyboard is open, or the device
// is being held in a horizontal position.
- boolean screenOnImmediately = (CallAudioState.ROUTE_WIRED_HEADSET == audioMode
- || CallAudioState.ROUTE_SPEAKER == audioMode
- || CallAudioState.ROUTE_BLUETOOTH == audioMode
+ boolean screenOnImmediately = (CallAudioStateCompat.ROUTE_WIRED_HEADSET == audioMode
+ || CallAudioStateCompat.ROUTE_SPEAKER == audioMode
+ || CallAudioStateCompat.ROUTE_BLUETOOTH == audioMode
|| mIsHardKeyboardOpen);
// We do not keep the screen off when the user is outside in-call screen and we are
@@ -254,7 +254,7 @@
.add("offhook", mIsPhoneOffhook ? 1 : 0)
.add("hor", horizontal ? 1 : 0)
.add("ui", mUiShowing ? 1 : 0)
- .add("aud", CallAudioState.audioRouteToString(audioMode))
+ .add("aud", CallAudioStateCompat.audioRouteToString(audioMode))
.toString());
if (mIsPhoneOffhook && !screenOnImmediately) {
diff --git a/InCallUI/src/com/android/incallui/VideoCallFragment.java b/InCallUI/src/com/android/incallui/VideoCallFragment.java
index ce14e48..2c06303 100644
--- a/InCallUI/src/com/android/incallui/VideoCallFragment.java
+++ b/InCallUI/src/com/android/incallui/VideoCallFragment.java
@@ -31,6 +31,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import com.android.phone.common.animation.AnimUtils;
import com.google.common.base.Objects;
/**
@@ -105,6 +106,8 @@
*/
private boolean mIsLandscape;
+ private int mAnimationDuration;
+
/**
* Inner-class representing a {@link TextureView} and its associated {@link SurfaceTexture} and
* {@link Surface}. Used to manage the lifecycle of these objects across device orientation
@@ -419,6 +422,8 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ mAnimationDuration = getResources().getInteger(R.integer.video_animation_duration);
}
/**
@@ -454,9 +459,16 @@
*/
private void centerDisplayView(View displayVideo) {
if (!mIsLandscape) {
+ ViewGroup.LayoutParams p = displayVideo.getLayoutParams();
+ int height = p.height;
+
float spaceBesideCallCard = InCallPresenter.getInstance().getSpaceBesideCallCard();
- float videoViewTranslation = displayVideo.getHeight() / 2
- - spaceBesideCallCard / 2;
+ // If space beside call card is zeo, layout hasn't happened yet so there is no point
+ // in attempting to center the view.
+ if (Math.abs(spaceBesideCallCard - 0.0f) < 0.0001) {
+ return;
+ }
+ float videoViewTranslation = height / 2 - spaceBesideCallCard / 2;
displayVideo.setTranslationY(videoViewTranslation);
}
}
@@ -581,6 +593,30 @@
return mPreviewPhoto;
}
+ /**
+ * Adjusts the location of the video preview view by the specified offset.
+ *
+ * @param shiftUp {@code true} if the preview should shift up, {@code false} if it should shift
+ * down.
+ * @param offset The offset.
+ */
+ @Override
+ public void adjustPreviewLocation(boolean shiftUp, int offset) {
+ if (sPreviewSurface == null || mPreviewVideoContainer == null) {
+ return;
+ }
+
+ // Set the position of the secondary call info card to its starting location.
+ mPreviewVideoContainer.setTranslationY(shiftUp ? 0 : -offset);
+
+ // Animate the secondary card info slide up/down as it appears and disappears.
+ mPreviewVideoContainer.animate()
+ .setInterpolator(AnimUtils.EASE_OUT_EASE_IN)
+ .setDuration(mAnimationDuration)
+ .translationY(shiftUp ? -offset : 0)
+ .start();
+ }
+
private void onPresenterChanged(VideoCallPresenter presenter) {
Log.d(this, "onPresenterChanged: Presenter=" + presenter);
if (sDisplaySurface != null) {
@@ -668,6 +704,33 @@
}
}
+ /**
+ * Sets the rotation of the preview surface. Called when the dimensions change due to a
+ * device orientation change.
+ *
+ * Please note that the screen orientation passed in is subtracted from 360 to get the actual
+ * preview rotation values.
+ *
+ * @param rotation The screen orientation. One of -
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
+ */
+ @Override
+ public void setPreviewRotation(int orientation) {
+ Log.d(this, "setPreviewRotation: orientation=" + orientation);
+ if (sPreviewSurface != null) {
+ TextureView preview = sPreviewSurface.getTextureView();
+
+ if (preview == null ) {
+ return;
+ }
+
+ preview.setRotation(orientation);
+ }
+ }
+
@Override
public void setPreviewSurfaceSize(int width, int height) {
final boolean isPreviewSurfaceAvailable = sPreviewSurface != null;
@@ -700,7 +763,7 @@
*/
@Override
public void setDisplayVideoSize(int width, int height) {
- Log.d(this, "setDisplayVideoSize: width=" + width + " height=" + height);
+ Log.v(this, "setDisplayVideoSize: width=" + width + " height=" + height);
if (sDisplaySurface != null) {
TextureView displayVideo = sDisplaySurface.getTextureView();
if (displayVideo == null) {
diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
index 84d9c97..621c699 100644
--- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java
@@ -24,7 +24,6 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.ContactsContract;
-import android.telecom.CallAudioState;
import android.telecom.Connection;
import android.telecom.InCallService.VideoCall;
import android.telecom.VideoProfile;
@@ -75,12 +74,14 @@
public static final boolean DEBUG = false;
/**
- * Runnable which is posted to schedule automatically entering fullscreen mode.
+ * Runnable which is posted to schedule automatically entering fullscreen mode. Will not auto
+ * enter fullscreen mode if the dialpad is visible (doing so would make it impossible to exit
+ * the dialpad).
*/
private Runnable mAutoFullscreenRunnable = new Runnable() {
@Override
public void run() {
- if (mAutoFullScreenPending) {
+ if (mAutoFullScreenPending && !InCallPresenter.getInstance().isDialpadVisible()) {
Log.v(this, "Automatically entering fullscreen mode.");
InCallPresenter.getInstance().setFullScreen(true);
mAutoFullScreenPending = false;
@@ -153,18 +154,13 @@
/**
* Determines the device orientation (portrait/lanscape).
*/
- private int mDeviceOrientation;
+ private int mDeviceOrientation = InCallOrientationEventListener.SCREEN_ORIENTATION_0;
/**
* Tracks the state of the preview surface negotiation with the telephony layer.
*/
private int mPreviewSurfaceState = PreviewSurfaceState.NONE;
- /**
- * Saves the audio mode which was selected prior to going into a video call.
- */
- private static int sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID;
-
private static boolean mIsVideoMode = false;
/**
@@ -239,6 +235,7 @@
InCallPresenter.getInstance().addOrientationListener(this);
// To get updates of video call details changes
InCallPresenter.getInstance().addDetailsListener(this);
+ InCallPresenter.getInstance().addInCallEventListener(this);
// Register for surface and video events from {@link InCallVideoCallListener}s.
InCallVideoCallCallbackNotifier.getInstance().addSurfaceChangeListener(this);
@@ -266,6 +263,7 @@
InCallPresenter.getInstance().removeDetailsListener(this);
InCallPresenter.getInstance().removeIncomingCallListener(this);
InCallPresenter.getInstance().removeOrientationListener(this);
+ InCallPresenter.getInstance().removeInCallEventListener(this);
InCallVideoCallCallbackNotifier.getInstance().removeSurfaceChangeListener(this);
InCallVideoCallCallbackNotifier.getInstance().removeVideoEventListener(this);
@@ -404,8 +402,6 @@
" isVideoMode=" + isVideoMode());
if (newState == InCallPresenter.InCallState.NO_CALLS) {
- updateAudioMode(false);
-
if (isVideoMode()) {
exitVideoMode();
}
@@ -429,7 +425,7 @@
// change the camera or UI unless the waiting VT call becomes active.
primary = callList.getActiveCall();
currentCall = callList.getIncomingCall();
- if (!CallUtils.isActiveVideoCall(primary)) {
+ if (!VideoUtils.isActiveVideoCall(primary)) {
primary = callList.getIncomingCall();
}
} else if (newState == InCallPresenter.InCallState.OUTGOING) {
@@ -469,8 +465,23 @@
cancelAutoFullScreen();
}
+ /**
+ * Handles changes to the visibility of the secondary caller info bar.
+ *
+ * @param isVisible {@code true} if the secondary caller info is showing, {@code false}
+ * otherwise.
+ * @param height the height of the secondary caller info bar.
+ */
+ @Override
+ public void onSecondaryCallerInfoVisibilityChanged(boolean isVisible, int height) {
+ Log.d(this,
+ "onSecondaryCallerInfoVisibilityChanged : isVisible = " + isVisible + " height = "
+ + height);
+ getUi().adjustPreviewLocation(isVisible /* shiftUp */, height);
+ }
+
private void checkForVideoStateChange(Call call) {
- final boolean isVideoCall = CallUtils.isVideoCall(call);
+ final boolean isVideoCall = VideoUtils.isVideoCall(call);
final boolean hasVideoStateChanged = mCurrentVideoState != call.getVideoState();
Log.d(this, "checkForVideoStateChange: isVideoCall= " + isVideoCall
@@ -493,7 +504,7 @@
}
private void checkForCallStateChange(Call call) {
- final boolean isVideoCall = CallUtils.isVideoCall(call);
+ final boolean isVideoCall = VideoUtils.isVideoCall(call);
final boolean hasCallStateChanged = mCurrentCallState != call.getState();
Log.d(this, "checkForCallStateChange: isVideoCall= " + isVideoCall
@@ -512,7 +523,7 @@
updateCameraSelection(call);
String newCameraId = cameraManager.getActiveCameraId();
- if (!Objects.equals(prevCameraId, newCameraId) && CallUtils.isActiveVideoCall(call)) {
+ if (!Objects.equals(prevCameraId, newCameraId) && VideoUtils.isActiveVideoCall(call)) {
enableCamera(call.getVideoCall(), true);
}
}
@@ -531,7 +542,7 @@
}
private void onPrimaryCallChanged(Call newPrimaryCall) {
- final boolean isVideoCall = CallUtils.isVideoCall(newPrimaryCall);
+ final boolean isVideoCall = VideoUtils.isVideoCall(newPrimaryCall);
final boolean isVideoMode = isVideoMode();
Log.d(this, "onPrimaryCallChanged: isVideoCall=" + isVideoCall + " isVideoMode="
@@ -584,7 +595,7 @@
}
// If the details change is not for the currently active call no update is required.
if (!call.equals(mPrimaryCall)) {
- Log.d(this," onDetailsChanged: Details not for current active call so returning. ");
+ Log.d(this, " onDetailsChanged: Details not for current active call so returning. ");
return;
}
@@ -601,7 +612,8 @@
}
private void checkForOrientationAllowedChange(Call call) {
- InCallPresenter.getInstance().setInCallAllowsOrientationChange(CallUtils.isVideoCall(call));
+ InCallPresenter.getInstance().setInCallAllowsOrientationChange(
+ VideoUtils.isVideoCall(call));
}
/**
@@ -640,7 +652,7 @@
return;
}
- if (CallUtils.isVideoCall(call) && hasChanged) {
+ if (VideoUtils.isVideoCall(call) && hasChanged) {
enterVideoMode(call);
}
}
@@ -679,56 +691,16 @@
videoCall.setDisplaySurface(ui.getDisplayVideoSurface());
}
- final int rotation = ui.getCurrentRotation();
- if (rotation != VideoCallFragment.ORIENTATION_UNKNOWN) {
- videoCall.setDeviceOrientation(InCallPresenter.toRotationAngle(rotation));
- }
-
+ videoCall.setDeviceOrientation(mDeviceOrientation);
enableCamera(videoCall, isCameraRequired(newVideoState));
}
mCurrentVideoState = newVideoState;
- updateAudioMode(true);
mIsVideoMode = true;
maybeAutoEnterFullscreen(call);
}
- //TODO: Move this into Telecom. InCallUI should not be this close to audio functionality.
- private void updateAudioMode(boolean enableSpeaker) {
- if (!isSpeakerEnabledForVideoCalls()) {
- Log.d(this, "Speaker is disabled. Can't update audio mode");
- return;
- }
-
- final TelecomAdapter telecomAdapter = TelecomAdapter.getInstance();
- final boolean isPrevAudioModeValid =
- sPrevVideoAudioMode != AudioModeProvider.AUDIO_MODE_INVALID;
-
- Log.d(this, "Is previous audio mode valid = " + isPrevAudioModeValid + " enableSpeaker is "
- + enableSpeaker);
-
- // Set audio mode to previous mode if enableSpeaker is false.
- if (isPrevAudioModeValid && !enableSpeaker) {
- telecomAdapter.setAudioRoute(sPrevVideoAudioMode);
- sPrevVideoAudioMode = AudioModeProvider.AUDIO_MODE_INVALID;
- return;
- }
-
- int currentAudioMode = AudioModeProvider.getInstance().getAudioMode();
-
- // Set audio mode to speaker if enableSpeaker is true and bluetooth or headset are not
- // connected and it's a video call.
- if (!isAudioRouteEnabled(currentAudioMode,
- CallAudioState.ROUTE_BLUETOOTH | CallAudioState.ROUTE_WIRED_HEADSET) &&
- !isPrevAudioModeValid && enableSpeaker && CallUtils.isVideoCall(mPrimaryCall)) {
- sPrevVideoAudioMode = currentAudioMode;
-
- Log.d(this, "Routing audio to speaker");
- telecomAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
- }
- }
-
private static boolean isSpeakerEnabledForVideoCalls() {
// TODO: Make this a carrier configurable setting. For now this is always true. b/20090407
return true;
@@ -991,23 +963,39 @@
/**
* Handles changes to the device orientation.
- *
- * @param orientation The device orientation (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
+ * @param orientation The screen orientation of the device (one of:
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
*/
@Override
public void onDeviceOrientationChanged(int orientation) {
mDeviceOrientation = orientation;
- Point previewDimensions = getUi().getPreviewSize();
+
+ VideoCallUi ui = getUi();
+ if (ui == null) {
+ Log.e(this, "onDeviceOrientationChanged: VideoCallUi is null");
+ return;
+ }
+
+ Point previewDimensions = ui.getPreviewSize();
if (previewDimensions == null) {
return;
}
Log.d(this, "onDeviceOrientationChanged: orientation=" + orientation + " size: "
+ previewDimensions);
changePreviewDimensions(previewDimensions.x, previewDimensions.y);
+
+ ui.setPreviewRotation(mDeviceOrientation);
}
+ /**
+ * Handles an incoming upgrade to video request.
+ *
+ * @param call The call the request was received for.
+ * @param videoState The video state that the request wants to upgrade to.
+ */
@Override
public void onUpgradeToVideoRequest(Call call, int videoState) {
Log.d(this, "onUpgradeToVideoRequest call = " + call + " new video state = " + videoState);
@@ -1019,7 +1007,7 @@
return;
}
- call.setSessionModificationTo(videoState);
+ call.setRequestedVideoState(videoState);
}
@Override
@@ -1055,10 +1043,12 @@
/**
* Sets the preview surface size based on the current device orientation.
+ * See: {@link InCallOrientationEventListener#SCREEN_ORIENTATION_0},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_90},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_180},
+ * {@link InCallOrientationEventListener#SCREEN_ORIENTATION_270}).
*
- * @param orientation The device orientation (one of: {@link Surface#ROTATION_0},
- * {@link Surface#ROTATION_90}, {@link Surface#ROTATION_180},
- * {@link Surface#ROTATION_270}).
+ * @param orientation The device orientation
* @param aspectRatio The aspect ratio of the camera (width / height).
*/
private void setPreviewSize(int orientation, float aspectRatio) {
@@ -1070,8 +1060,8 @@
int height;
int width;
- if (orientation == Surface.ROTATION_90 || orientation == Surface.ROTATION_270) {
- // Landscape or reverse landscape orientation.
+ if (orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_90 ||
+ orientation == InCallOrientationEventListener.SCREEN_ORIENTATION_270) {
width = (int) (mMinimumVideoDimension * aspectRatio);
height = (int) mMinimumVideoDimension;
} else {
@@ -1089,7 +1079,7 @@
* @param height peer height
*/
private void setDisplayVideoSize(int width, int height) {
- Log.d(this, "setDisplayVideoSize:Received peer width=" + width + " peer height=" + height);
+ Log.v(this, "setDisplayVideoSize: Received peer width=" + width + " height=" + height);
VideoCallUi ui = getUi();
if (ui == null) {
return;
@@ -1097,7 +1087,7 @@
// Get current display size
Point size = ui.getScreenSize();
- Log.d("VideoCallPresenter", "setDisplayVideoSize: windowmgr width=" + size.x
+ Log.v(this, "setDisplayVideoSize: windowmgr width=" + size.x
+ " windowmgr height=" + size.y);
if (size.y * width > size.x * height) {
// current display height is too much. Correct it
@@ -1119,7 +1109,7 @@
return;
}
- if (!CallUtils.isVideoCall(call) || call.getState() == Call.State.INCOMING) {
+ if (!VideoUtils.isVideoCall(call) || call.getState() == Call.State.INCOMING) {
InCallPresenter.getInstance().setFullScreen(false);
}
}
@@ -1141,7 +1131,7 @@
if (call == null || (
call != null && (call.getState() != Call.State.ACTIVE ||
- !CallUtils.isVideoCall(call)) ||
+ !VideoUtils.isVideoCall(call)) ||
InCallPresenter.getInstance().isFullscreen())) {
// Ensure any previously scheduled attempt to enter fullscreen is cancelled.
cancelAutoFullScreen();
@@ -1189,7 +1179,7 @@
}
// Clear camera direction if this is not a video call.
- else if (CallUtils.isAudioCall(call)) {
+ else if (VideoUtils.isAudioCall(call)) {
cameraDir = Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
call.getVideoSettings().setCameraDir(cameraDir);
}
@@ -1197,33 +1187,33 @@
// If this is a waiting video call, default to active call's camera,
// since we don't want to change the current camera for waiting call
// without user's permission.
- else if (CallUtils.isVideoCall(activeCall) && CallUtils.isIncomingVideoCall(call)) {
+ else if (VideoUtils.isVideoCall(activeCall) && VideoUtils.isIncomingVideoCall(call)) {
cameraDir = activeCall.getVideoSettings().getCameraDir();
}
// Infer the camera direction from the video state and store it,
// if this is an outgoing video call.
- else if (CallUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call) ) {
+ else if (VideoUtils.isOutgoingVideoCall(call) && !isCameraDirectionSet(call) ) {
cameraDir = toCameraDirection(call.getVideoState());
call.getVideoSettings().setCameraDir(cameraDir);
}
// Use the stored camera dir if this is an outgoing video call for which camera direction
// is set.
- else if (CallUtils.isOutgoingVideoCall(call)) {
+ else if (VideoUtils.isOutgoingVideoCall(call)) {
cameraDir = call.getVideoSettings().getCameraDir();
}
// Infer the camera direction from the video state and store it,
// if this is an active video call and camera direction is not set.
- else if (CallUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) {
+ else if (VideoUtils.isActiveVideoCall(call) && !isCameraDirectionSet(call)) {
cameraDir = toCameraDirection(call.getVideoState());
call.getVideoSettings().setCameraDir(cameraDir);
}
// Use the stored camera dir if this is an active video call for which camera direction
// is set.
- else if (CallUtils.isActiveVideoCall(call)) {
+ else if (VideoUtils.isActiveVideoCall(call)) {
cameraDir = call.getVideoSettings().getCameraDir();
}
@@ -1248,7 +1238,7 @@
}
private static boolean isCameraDirectionSet(Call call) {
- return CallUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir()
+ return VideoUtils.isVideoCall(call) && call.getVideoSettings().getCameraDir()
!= Call.VideoSettings.CAMERA_DIRECTION_UNKNOWN;
}
@@ -1354,5 +1344,7 @@
Point getPreviewSize();
void cleanupSurfaces();
ImageView getPreviewPhotoView();
+ void adjustPreviewLocation(boolean shiftUp, int offset);
+ void setPreviewRotation(int orientation);
}
}
diff --git a/InCallUI/src/com/android/incallui/VideoPauseController.java b/InCallUI/src/com/android/incallui/VideoPauseController.java
index 54e31f8..070448e 100644
--- a/InCallUI/src/com/android/incallui/VideoPauseController.java
+++ b/InCallUI/src/com/android/incallui/VideoPauseController.java
@@ -1,34 +1,21 @@
-/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (C) 2015 The Android Open Source Project
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- * * Neither the name of The Linux Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
*
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
*/
package com.android.incallui;
-import android.telecom.VideoProfile;
import com.android.incallui.Call.State;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
@@ -42,7 +29,7 @@
*/
class VideoPauseController implements InCallStateListener, IncomingCallListener,
SessionModificationListener {
- private static final String TAG = "VideoPauseController:";
+ private static final String TAG = "VideoPauseController";
/**
* Keeps track of the current active/foreground call.
@@ -168,7 +155,7 @@
}
boolean hasPrimaryCallChanged = !areSame(call, mPrimaryCallContext);
- boolean canVideoPause = CallUtils.canVideoPause(call);
+ boolean canVideoPause = VideoUtils.canVideoPause(call);
log("onStateChange, hasPrimaryCallChanged=" + hasPrimaryCallChanged);
log("onStateChange, canVideoPause=" + canVideoPause);
log("onStateChange, IsInBackground=" + mIsInBackground);
@@ -206,7 +193,7 @@
log("onPrimaryCallChanged, IsInBackground=" + mIsInBackground);
Preconditions.checkState(!areSame(call, mPrimaryCallContext));
- final boolean canVideoPause = CallUtils.canVideoPause(call);
+ final boolean canVideoPause = VideoUtils.canVideoPause(call);
if ((isIncomingCall(mPrimaryCallContext) || isDialing(mPrimaryCallContext))
&& canVideoPause && !mIsInBackground) {
@@ -366,10 +353,10 @@
if (resume) {
log("sending resume request, call=" + call);
call.getVideoCall()
- .sendSessionModifyRequest(CallUtils.makeVideoUnPauseProfile(call));
+ .sendSessionModifyRequest(VideoUtils.makeVideoUnPauseProfile(call));
} else {
log("sending pause request, call=" + call);
- call.getVideoCall().sendSessionModifyRequest(CallUtils.makeVideoPauseProfile(call));
+ call.getVideoCall().sendSessionModifyRequest(VideoUtils.makeVideoPauseProfile(call));
}
}
@@ -407,7 +394,7 @@
* @return {@code true} if the call is a video call, {@code false} otherwise.
*/
private static boolean isVideoCall(CallContext callContext) {
- return callContext != null && CallUtils.isVideoCall(callContext.getVideoState());
+ return callContext != null && VideoUtils.isVideoCall(callContext.getVideoState());
}
/**
diff --git a/InCallUI/src/com/android/incallui/VideoUtils.java b/InCallUI/src/com/android/incallui/VideoUtils.java
new file mode 100644
index 0000000..73eb3a9
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/VideoUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.incallui;
+
+import android.telecom.VideoProfile;
+
+import com.android.dialer.compat.DialerCompatUtils;
+
+import com.google.common.base.Preconditions;
+
+public class VideoUtils {
+
+ public static boolean isVideoCall(Call call) {
+ return call != null && isVideoCall(call.getVideoState());
+ }
+
+ public static boolean isVideoCall(int videoState) {
+ if (!DialerCompatUtils.isVideoCompatible()) {
+ return false;
+ }
+
+ return VideoProfile.isTransmissionEnabled(videoState)
+ || VideoProfile.isReceptionEnabled(videoState);
+ }
+
+ public static boolean isBidirectionalVideoCall(Call call) {
+ if (!DialerCompatUtils.isVideoCompatible()) {
+ return false;
+ }
+
+ return VideoProfile.isBidirectional(call.getVideoState());
+ }
+
+ public static boolean isIncomingVideoCall(Call call) {
+ if (!VideoUtils.isVideoCall(call)) {
+ return false;
+ }
+ final int state = call.getState();
+ return (state == Call.State.INCOMING) || (state == Call.State.CALL_WAITING);
+ }
+
+ public static boolean isActiveVideoCall(Call call) {
+ return VideoUtils.isVideoCall(call) && call.getState() == Call.State.ACTIVE;
+ }
+
+ public static boolean isOutgoingVideoCall(Call call) {
+ if (!VideoUtils.isVideoCall(call)) {
+ return false;
+ }
+ final int state = call.getState();
+ return Call.State.isDialing(state) || state == Call.State.CONNECTING
+ || state == Call.State.SELECT_PHONE_ACCOUNT;
+ }
+
+ public static boolean isAudioCall(Call call) {
+ if (!DialerCompatUtils.isVideoCompatible()) {
+ return true;
+ }
+
+ return call != null && VideoProfile.isAudioOnly(call.getVideoState());
+ }
+
+ // TODO (ims-vt) Check if special handling is needed for CONF calls.
+ public static boolean canVideoPause(Call call) {
+ return isVideoCall(call) && call.getState() == Call.State.ACTIVE;
+ }
+
+ public static VideoProfile makeVideoPauseProfile(Call call) {
+ Preconditions.checkNotNull(call);
+ Preconditions.checkState(!VideoProfile.isAudioOnly(call.getVideoState()));
+ return new VideoProfile(getPausedVideoState(call.getVideoState()));
+ }
+
+ public static VideoProfile makeVideoUnPauseProfile(Call call) {
+ Preconditions.checkNotNull(call);
+ return new VideoProfile(getUnPausedVideoState(call.getVideoState()));
+ }
+
+ public static int getUnPausedVideoState(int videoState) {
+ return videoState & (~VideoProfile.STATE_PAUSED);
+ }
+
+ public static int getPausedVideoState(int videoState) {
+ return videoState | VideoProfile.STATE_PAUSED;
+ }
+
+}