From 9c98346835b8e902e0c2c11f14a35f4e91578e9f Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Mon, 11 Aug 2014 14:44:18 -0700 Subject: Add UI/functions to handle a video upgrade request. + Move session modification listener into the video call presenter. + Add new video accept/reject notification and full-screen UI. + Added "handling" which sends session modification response. Bug: 16657915 Change-Id: I35ff04e383d4938cbfb4e76677730f82557fd918 --- .../res/drawable/ic_lockscreen_decline_video.xml | 28 +++++++++ ...ic_lockscreen_decline_video_activated_layer.xml | 25 ++++++++ .../ic_lockscreen_decline_video_normal_layer.xml | 33 +++++++++++ InCallUI/res/values/array.xml | 24 ++++++++ InCallUI/res/values/strings.xml | 6 ++ .../src/com/android/incallui/AnswerFragment.java | 14 ++++- .../src/com/android/incallui/AnswerPresenter.java | 29 ++++++++-- InCallUI/src/com/android/incallui/Call.java | 14 ++++- .../com/android/incallui/CallCardPresenter.java | 64 ++------------------- InCallUI/src/com/android/incallui/CallList.java | 15 +++++ .../src/com/android/incallui/GlowPadWrapper.java | 10 +++- InCallUI/src/com/android/incallui/InCallApp.java | 8 +++ .../src/com/android/incallui/InCallPresenter.java | 32 +++++++++++ .../com/android/incallui/StatusBarNotifier.java | 67 +++++++++++++++++----- .../com/android/incallui/VideoCallPresenter.java | 48 +++++++++++++++- 15 files changed, 333 insertions(+), 84 deletions(-) create mode 100644 InCallUI/res/drawable/ic_lockscreen_decline_video.xml create mode 100644 InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml create mode 100644 InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml (limited to 'InCallUI') diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video.xml new file mode 100644 index 000000000..cedd49757 --- /dev/null +++ b/InCallUI/res/drawable/ic_lockscreen_decline_video.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml new file mode 100644 index 000000000..63fe4cad8 --- /dev/null +++ b/InCallUI/res/drawable/ic_lockscreen_decline_video_activated_layer.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml b/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml new file mode 100644 index 000000000..370d515e7 --- /dev/null +++ b/InCallUI/res/drawable/ic_lockscreen_decline_video_normal_layer.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/InCallUI/res/values/array.xml b/InCallUI/res/values/array.xml index b741905dd..5270de1ac 100644 --- a/InCallUI/res/values/array.xml +++ b/InCallUI/res/values/array.xml @@ -111,4 +111,28 @@ @string/description_direction_left @string/description_direction_down + + + + + @drawable/ic_lockscreen_answer_video + @null + @drawable/ic_lockscreen_decline + @null" + + + @string/description_target_accept_upgrade_to_video_request + @null + @string/description_target_decline_upgrade_to_video_request + @null" + + + @string/description_direction_right + @null + @string/description_direction_left + @null + diff --git a/InCallUI/res/values/strings.xml b/InCallUI/res/values/strings.xml index 1ce382e42..964286aa0 100644 --- a/InCallUI/res/values/strings.xml +++ b/InCallUI/res/values/strings.xml @@ -504,6 +504,12 @@ Answer as audio call + + Accept video request + + Decline video request Slide up for %s. diff --git a/InCallUI/src/com/android/incallui/AnswerFragment.java b/InCallUI/src/com/android/incallui/AnswerFragment.java index db441237f..4fb79ac06 100644 --- a/InCallUI/src/com/android/incallui/AnswerFragment.java +++ b/InCallUI/src/com/android/incallui/AnswerFragment.java @@ -18,6 +18,7 @@ package com.android.incallui; import android.app.AlertDialog; import android.app.Dialog; +import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.text.Editable; @@ -48,6 +49,7 @@ public class AnswerFragment extends BaseFragment super.onUiReady(ui); final CallList calls = CallList.getInstance(); - final Call call = calls.getIncomingCall(); - // TODO: change so that answer presenter never starts up if it's not incoming. + Call call; + call = calls.getIncomingCall(); if (call != null) { processIncomingCall(call); } + call = calls.getVideoUpgradeRequestCall(); + if (call != null) { + processVideoUpgradeRequestCall(call); + } // Listen for incoming calls. calls.addListener(this); @@ -111,6 +116,17 @@ public class AnswerPresenter extends Presenter } } + private void processVideoUpgradeRequestCall(Call call) { + mCallId = call.getId(); + mCall = call; + + // Listen for call updates for the current call. + CallList.getInstance().addCallUpdateListener(mCallId, this); + getUi().showAnswerUi(true); + + getUi().showTargets(AnswerFragment.TARGET_SET_FOR_VIDEO_UPGRADE_REQUEST); + } + @Override public void onCallChanged(Call call) { Log.d(this, "onCallStateChange() " + call + " " + this); @@ -126,13 +142,18 @@ public class AnswerPresenter extends Presenter } } - public void onAnswer(int videoState) { + public void onAnswer(int videoState, Context context) { if (mCallId == null) { return; } Log.d(this, "onAnswer " + mCallId); - TelecommAdapter.getInstance().answerCall(mCall.getId(), videoState); + if (mCall.getSessionModificationState() + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + InCallPresenter.getInstance().acceptUpgradeRequest(context); + } else { + TelecommAdapter.getInstance().answerCall(mCall.getId(), videoState); + } } /** diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java index fcdf09223..2a97a3f5e 100644 --- a/InCallUI/src/com/android/incallui/Call.java +++ b/InCallUI/src/com/android/incallui/Call.java @@ -109,7 +109,8 @@ public final class Call { public static class SessionModificationState { public static final int NO_REQUEST = 0; public static final int WAITING_FOR_RESPONSE = 1; - public static final int REQUEST_FAILED = 1; + public static final int REQUEST_FAILED = 2; + public static final int RECEIVED_UPGRADE_TO_VIDEO_REQUEST = 3; } private static final String ID_PREFIX = Call.class.getSimpleName() + "_"; @@ -362,6 +363,17 @@ public final class Call { } } + public static boolean areSame(Call call1, Call call2) { + if (call1 == null && call2 == null) { + return true; + } else if (call1 == null || call2 == null) { + return false; + } + + // otherwise compare call Ids + return call1.getId().equals(call2.getId()); + } + public int getSessionModificationState() { return mSessionModificationState; } diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java index 92412ea2e..e6ef71355 100644 --- a/InCallUI/src/com/android/incallui/CallCardPresenter.java +++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java @@ -22,7 +22,6 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; -import android.os.Handler; import android.telecomm.CallCapabilities; import android.telecomm.PhoneAccount; import android.telecomm.PhoneAccountHandle; @@ -54,18 +53,16 @@ import com.google.common.base.Preconditions; */ public class CallCardPresenter extends Presenter implements InCallStateListener, IncomingCallListener, InCallDetailsListener, - InCallEventListener, InCallVideoCallListenerNotifier.SessionModificationListener { + InCallEventListener { private static final String TAG = CallCardPresenter.class.getSimpleName(); private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000; - private static final long SESSION_MODIFICATION_RESET_DELAY_MS = 3000; private Call mPrimary; private Call mSecondary; private ContactCacheEntry mPrimaryContactInfo; private ContactCacheEntry mSecondaryContactInfo; - private CallTimer mCallTimer; - private Handler mSessionModificationResetHandler; + private CallTimer mCallT\imer; private Context mContext; private TelecommManager mTelecommManager; @@ -104,7 +101,6 @@ public class CallCardPresenter extends Presenter updateCallTime(); } }); - mSessionModificationResetHandler = new Handler(); } public void init(Context context, Call call) { @@ -137,16 +133,12 @@ public class CallCardPresenter extends Presenter InCallPresenter.getInstance().addIncomingCallListener(this); InCallPresenter.getInstance().addDetailsListener(this); InCallPresenter.getInstance().addInCallEventListener(this); - - InCallVideoCallListenerNotifier.getInstance().addSessionModificationListener(this); } @Override public void onUiUnready(CallCardUi ui) { super.onUiUnready(ui); - InCallVideoCallListenerNotifier.getInstance().removeSessionModificationListener(this); - // stop getting call state changes InCallPresenter.getInstance().removeListener(this); InCallPresenter.getInstance().removeIncomingCallListener(this); @@ -196,8 +188,9 @@ public class CallCardPresenter extends Presenter final boolean outgoingCallReady = newState == InCallState.OUTGOING && oldState == InCallState.PENDING_OUTGOING; - final boolean primaryChanged = !areCallsSame(mPrimary, primary) || outgoingCallReady; - final boolean secondaryChanged = !areCallsSame(mSecondary, secondary); + final boolean primaryChanged = !Call.areSame(mPrimary, primary); + final boolean secondaryChanged = !Call.areSame(mSecondary, secondary); + mSecondary = secondary; mPrimary = primary; @@ -364,53 +357,6 @@ public class CallCardPresenter extends Presenter TelecommAdapter.getInstance().phoneAccountClicked(mPrimary.getId()); } - @Override - public void onUpgradeToVideoRequest(Call call) { - // Implementing to satsify interface. - } - - @Override - public void onUpgradeToVideoSuccess(Call call) { - if (mPrimary == null || !areCallsSame(mPrimary, call)) { - return; - } - - mPrimary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); - } - - @Override - public void onUpgradeToVideoFail(Call call) { - if (mPrimary == null || !areCallsSame(mPrimary, call)) { - return; - } - - call.setSessionModificationState(Call.SessionModificationState.REQUEST_FAILED); - - // Start handler to change state from REQUEST_FAILED to NO_REQUEST after an interval. - mSessionModificationResetHandler.postDelayed(new Runnable() { - @Override - public void run() { - mPrimary.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); - } - }, SESSION_MODIFICATION_RESET_DELAY_MS); - } - - @Override - public void onDowngradeToAudio(Call call) { - // Implementing to satsify interface. - } - - private boolean areCallsSame(Call call1, Call call2) { - if (call1 == null && call2 == null) { - return true; - } else if (call1 == null || call2 == null) { - return false; - } - - // otherwise compare call Ids - return call1.getId().equals(call2.getId()); - } - private void maybeStartSearch(Call call, boolean isPrimary) { // no need to start search for conference calls which show generic info. if (call != null && !call.isConferenceCall()) { diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java index becb411ca..66103e6ff 100644 --- a/InCallUI/src/com/android/incallui/CallList.java +++ b/InCallUI/src/com/android/incallui/CallList.java @@ -273,6 +273,21 @@ public class CallList implements InCallPhoneListener { return result; } + /** + * Returns the first call found in the call map with the specified call modification state. + * @param state The session modification state to search for. + * @return The first call with the specified state. + */ + public Call getVideoUpgradeRequestCall() { + for(Call call : mCallById.values()) { + if (call.getSessionModificationState() == + Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + return call; + } + } + return null; + } + public Call getCallById(String callId) { return mCallById.get(callId); } diff --git a/InCallUI/src/com/android/incallui/GlowPadWrapper.java b/InCallUI/src/com/android/incallui/GlowPadWrapper.java index 859cd64bd..1617d06bc 100644 --- a/InCallUI/src/com/android/incallui/GlowPadWrapper.java +++ b/InCallUI/src/com/android/incallui/GlowPadWrapper.java @@ -112,7 +112,7 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger final int resId = getResourceIdForTarget(target); switch (resId) { case R.drawable.ic_lockscreen_answer: - mAnswerListener.onAnswer(VideoCallProfile.VideoState.AUDIO_ONLY); + mAnswerListener.onAnswer(VideoCallProfile.VideoState.AUDIO_ONLY, getContext()); mTargetTriggered = true; break; case R.drawable.ic_lockscreen_decline: @@ -124,7 +124,11 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger mTargetTriggered = true; break; case R.drawable.ic_videocam: - mAnswerListener.onAnswer(VideoCallProfile.VideoState.BIDIRECTIONAL); + mAnswerListener.onAnswer(VideoCallProfile.VideoState.BIDIRECTIONAL, getContext()); + mTargetTriggered = true; + break; + case R.drawable.ic_toolbar_video_off: + InCallPresenter.getInstance().declineUpgradeRequest(getContext()); mTargetTriggered = true; break; default: @@ -148,7 +152,7 @@ public class GlowPadWrapper extends GlowPadView implements GlowPadView.OnTrigger } public interface AnswerListener { - void onAnswer(int videoState); + void onAnswer(int videoState, Context context); void onDecline(); void onText(); } diff --git a/InCallUI/src/com/android/incallui/InCallApp.java b/InCallUI/src/com/android/incallui/InCallApp.java index c7bb4fb2a..78faf1f2b 100644 --- a/InCallUI/src/com/android/incallui/InCallApp.java +++ b/InCallUI/src/com/android/incallui/InCallApp.java @@ -41,6 +41,10 @@ public class InCallApp extends Application { "com.android.incallui.ACTION_ANSWER_VIDEO_INCOMING_CALL"; public static final String ACTION_ANSWER_VOICE_INCOMING_CALL = "com.android.incallui.ACTION_ANSWER_VOICE_INCOMING_CALL"; + public static final String ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST = + "com.android.incallui.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST"; + public static final String ACTION_DECLINE_VIDEO_UPGRADE_REQUEST = + "com.android.incallui.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST"; public InCallApp() { } @@ -76,6 +80,10 @@ public class InCallApp extends Application { InCallPresenter.getInstance().declineIncomingCall(context); } else if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) { InCallPresenter.getInstance().hangUpOngoingCall(context); + } else if (action.equals(ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST)) { + InCallPresenter.getInstance().acceptUpgradeRequest(context); + } else if (action.equals(ACTION_DECLINE_VIDEO_UPGRADE_REQUEST)) { + InCallPresenter.getInstance().declineUpgradeRequest(context); } } } diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java index cf43fbd66..79085acfe 100644 --- a/InCallUI/src/com/android/incallui/InCallPresenter.java +++ b/InCallUI/src/com/android/incallui/InCallPresenter.java @@ -525,6 +525,38 @@ public class InCallPresenter implements CallList.Listener, InCallPhoneListener { } } + public void acceptUpgradeRequest(Context context) { + // Bail if we have been shut down and the call list is null. + if (mCallList == null) { + StatusBarNotifier.clearInCallNotification(context); + return; + } + + Call call = mCallList.getVideoUpgradeRequestCall(); + if (call != null) { + VideoCallProfile videoProfile = + new VideoCallProfile(VideoCallProfile.VideoState.BIDIRECTIONAL); + call.getVideoCall().sendSessionModifyResponse(videoProfile); + call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); + } + } + + public void declineUpgradeRequest(Context context) { + // Bail if we have been shut down and the call list is null. + if (mCallList == null) { + StatusBarNotifier.clearInCallNotification(context); + return; + } + + Call call = mCallList.getVideoUpgradeRequestCall(); + if (call != null) { + VideoCallProfile videoProfile = + new VideoCallProfile(VideoCallProfile.VideoState.AUDIO_ONLY); + call.getVideoCall().sendSessionModifyResponse(videoProfile); + call.setSessionModificationState(Call.SessionModificationState.NO_REQUEST); + } + } + /** * Returns true if the incall app is the foreground application. */ diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java index f88d2b5c6..7efaa12b8 100644 --- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java +++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java @@ -258,12 +258,15 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { final int state = call.getState(); final boolean isConference = call.isConferenceCall(); + final boolean isVideoUpgradeRequest = call.getSessionModificationState() + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST; + + // Check if data has changed; if nothing is different, don't issue another notification. final int iconResId = getIconToDisplay(call); final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, isConference); final int contentResId = getContentString(call); final String contentTitle = getContentTitle(contactInfo, isConference); - // If we checked and found that nothing is different, dont issue another notification. if (!checkForChangeAndSaveData(iconResId, contentResId, largeIcon, contentTitle, state)) { return; } @@ -283,12 +286,33 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { configureFullScreenIntent(builder, inCallPendingIntent, call); } - // set the content + // Set the content builder.setContentText(mContext.getString(contentResId)); builder.setSmallIcon(iconResId); builder.setContentTitle(contentTitle); builder.setLargeIcon(largeIcon); + if (isVideoUpgradeRequest) { + builder.setUsesChronometer(false); + addDismissUpgradeRequestAction(builder); + addAcceptUpgradeRequestAction(builder); + } else { + createIncomingCallNotification(call, state, builder); + } + + addPersonReference(builder, contactInfo, call); + + /* + * Fire off the notification + */ + Notification notification = builder.build(); + Log.d(this, "Notifying IN_CALL_NOTIFICATION: " + notification); + mNotificationManager.notify(IN_CALL_NOTIFICATION, notification); + mIsShowingNotification = true; + } + + private void createIncomingCallNotification( + Call call, int state, Notification.Builder builder) { if (state == Call.State.ACTIVE) { builder.setUsesChronometer(true); builder.setWhen(call.getConnectTimeMillis()); @@ -310,16 +334,6 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { addAnswerAction(builder); } } - - addPersonReference(builder, contactInfo, call); - - /* - * Fire off the notification - */ - Notification notification = builder.build(); - Log.d(this, "Notifying IN_CALL_NOTIFICATION: " + notification); - mNotificationManager.notify(IN_CALL_NOTIFICATION, notification); - mIsShowingNotification = true; } /** @@ -422,6 +436,9 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { // display that regardless of the state of the other calls. if (call.getState() == Call.State.ONHOLD) { return R.drawable.stat_sys_phone_call_on_hold; + } else if (call.getSessionModificationState() + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + return R.drawable.ic_videocam; } return R.drawable.fab_ic_call; } @@ -434,12 +451,13 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { if (call.getState() == Call.State.INCOMING || call.getState() == Call.State.CALL_WAITING) { resId = R.string.notification_incoming_call; - } else if (call.getState() == Call.State.ONHOLD) { resId = R.string.notification_on_hold; - } else if (Call.State.isDialing(call.getState())) { resId = R.string.notification_dialing; + } else if (call.getSessionModificationState() + == Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) { + resId = R.string.notification_requesting_video_call; } return resId; @@ -456,6 +474,9 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { if (call == null) { call = callList.getOutgoingCall(); } + if (call == null) { + call = callList.getVideoUpgradeRequestCall(); + } if (call == null) { call = callList.getActiveOrBackgroundCall(); } @@ -512,6 +533,24 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener { answerVoicePendingIntent); } + private void addAcceptUpgradeRequestAction(Notification.Builder builder) { + Log.i(this, "Will show \"accept\" action in the incoming call Notification"); + + PendingIntent acceptVideoPendingIntent = createNotificationPendingIntent( + mContext, InCallApp.ACTION_ANSWER_VOICE_INCOMING_CALL); + builder.addAction(0, mContext.getText(R.string.notification_action_accept), + acceptVideoPendingIntent); + } + + private void addDismissUpgradeRequestAction(Notification.Builder builder) { + Log.i(this, "Will show \"dismiss\" action in the incoming call Notification"); + + PendingIntent declineVideoPendingIntent = createNotificationPendingIntent( + mContext, InCallApp.ACTION_ANSWER_VOICE_INCOMING_CALL); + builder.addAction(0, mContext.getText(R.string.notification_action_dismiss), + declineVideoPendingIntent); + } + /** * Adds fullscreen intent to the builder. */ diff --git a/InCallUI/src/com/android/incallui/VideoCallPresenter.java b/InCallUI/src/com/android/incallui/VideoCallPresenter.java index 025a863e2..73947e521 100644 --- a/InCallUI/src/com/android/incallui/VideoCallPresenter.java +++ b/InCallUI/src/com/android/incallui/VideoCallPresenter.java @@ -18,6 +18,7 @@ package com.android.incallui; import android.content.Context; import android.content.res.Configuration; +import android.os.Handler; import android.telecomm.CallAudioState; import android.telecomm.InCallService.VideoCall; import android.view.Surface; @@ -56,7 +57,8 @@ import java.util.Objects; */ public class VideoCallPresenter extends Presenter implements IncomingCallListener, InCallOrientationListener, InCallStateListener, - InCallDetailsListener, SurfaceChangeListener, VideoEventListener { + InCallDetailsListener, SurfaceChangeListener, VideoEventListener, + InCallVideoCallListenerNotifier.SessionModificationListener { /** * Determines the device orientation (portrait/lanscape). @@ -145,6 +147,10 @@ public class VideoCallPresenter extends Presenter