blob: 2595e2f8bd819d6604a44d076c5c42cf629278af [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.incallui;
18
19import android.support.annotation.NonNull;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070020import com.android.dialer.common.Assert;
21import com.android.dialer.common.LogUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import com.android.incallui.InCallPresenter.InCallState;
23import com.android.incallui.InCallPresenter.InCallStateListener;
24import com.android.incallui.InCallPresenter.IncomingCallListener;
25import com.android.incallui.call.CallList;
26import com.android.incallui.call.DialerCall;
27import com.android.incallui.call.DialerCall.State;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import java.util.Objects;
29
30/**
31 * This class is responsible for generating video pause/resume requests when the InCall UI is sent
32 * to the background and subsequently brought back to the foreground.
33 */
34class VideoPauseController implements InCallStateListener, IncomingCallListener {
Eric Erfanianccca3152017-02-22 16:32:36 -080035 private static VideoPauseController sVideoPauseController;
36 private InCallPresenter mInCallPresenter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070037
38 /** The current call, if applicable. */
39 private DialerCall mPrimaryCall = null;
40
41 /**
42 * The cached state of primary call, updated after onStateChange has processed.
43 *
44 * <p>These values are stored to detect specific changes in state between onStateChange calls.
45 */
46 private int mPrevCallState = State.INVALID;
47
48 private boolean mWasVideoCall = false;
49
Eric Erfanianccca3152017-02-22 16:32:36 -080050 /**
51 * Tracks whether the application is in the background. {@code True} if the application is in the
52 * background, {@code false} otherwise.
53 */
54 private boolean mIsInBackground = false;
55
56 /**
57 * Singleton accessor for the {@link VideoPauseController}.
58 *
59 * @return Singleton instance of the {@link VideoPauseController}.
60 */
61 /*package*/
62 static synchronized VideoPauseController getInstance() {
63 if (sVideoPauseController == null) {
64 sVideoPauseController = new VideoPauseController();
65 }
66 return sVideoPauseController;
67 }
68
Eric Erfaniand5e47f62017-03-15 14:41:07 -070069 private boolean wasIncomingCall() {
70 return (mPrevCallState == DialerCall.State.CALL_WAITING
71 || mPrevCallState == DialerCall.State.INCOMING);
Eric Erfanianccca3152017-02-22 16:32:36 -080072 }
73
74 /**
75 * Determines if a call is in incoming/waiting state.
76 *
77 * @param call The call.
78 * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
79 */
80 private static boolean isIncomingCall(DialerCall call) {
81 return call != null
82 && (call.getState() == DialerCall.State.CALL_WAITING
83 || call.getState() == DialerCall.State.INCOMING);
84 }
85
86 /**
87 * Determines if a call is dialing.
88 *
Eric Erfanianccca3152017-02-22 16:32:36 -080089 * @return {@code true} if the call is dialing, {@code false} otherwise.
90 */
Eric Erfaniand5e47f62017-03-15 14:41:07 -070091 private boolean wasDialing() {
92 return DialerCall.State.isDialing(mPrevCallState);
Eric Erfanianccca3152017-02-22 16:32:36 -080093 }
94
95 /**
96 * Configures the {@link VideoPauseController} to listen to call events. Configured via the {@link
97 * com.android.incallui.InCallPresenter}.
98 *
99 * @param inCallPresenter The {@link com.android.incallui.InCallPresenter}.
100 */
101 public void setUp(@NonNull InCallPresenter inCallPresenter) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700102 LogUtil.enterBlock("VideoPauseController.setUp");
103 mInCallPresenter = Assert.isNotNull(inCallPresenter);
Eric Erfanianccca3152017-02-22 16:32:36 -0800104 mInCallPresenter.addListener(this);
105 mInCallPresenter.addIncomingCallListener(this);
106 }
107
108 /**
109 * Cleans up the {@link VideoPauseController} by removing all listeners and clearing its internal
110 * state. Called from {@link com.android.incallui.InCallPresenter}.
111 */
112 public void tearDown() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700113 LogUtil.enterBlock("VideoPauseController.tearDown");
Eric Erfanianccca3152017-02-22 16:32:36 -0800114 mInCallPresenter.removeListener(this);
115 mInCallPresenter.removeIncomingCallListener(this);
116 clear();
117 }
118
119 /** Clears the internal state for the {@link VideoPauseController}. */
120 private void clear() {
121 mInCallPresenter = null;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700122 mPrimaryCall = null;
123 mPrevCallState = State.INVALID;
124 mWasVideoCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800125 mIsInBackground = false;
126 }
127
128 /**
129 * Handles changes in the {@link InCallState}. Triggers pause and resumption of video for the
130 * current foreground call.
131 *
132 * @param oldState The previous {@link InCallState}.
133 * @param newState The current {@link InCallState}.
134 * @param callList List of current call.
135 */
136 @Override
137 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800138 DialerCall call;
139 if (newState == InCallState.INCOMING) {
140 call = callList.getIncomingCall();
141 } else if (newState == InCallState.WAITING_FOR_ACCOUNT) {
142 call = callList.getWaitingForAccountCall();
143 } else if (newState == InCallState.PENDING_OUTGOING) {
144 call = callList.getPendingOutgoingCall();
145 } else if (newState == InCallState.OUTGOING) {
146 call = callList.getOutgoingCall();
147 } else {
148 call = callList.getActiveCall();
149 }
150
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700151 boolean hasPrimaryCallChanged = !Objects.equals(call, mPrimaryCall);
152 boolean canVideoPause = videoCanPause(call);
153
154 LogUtil.i(
155 "VideoPauseController.onStateChange",
156 "hasPrimaryCallChanged: %b, videoCanPause: %b, isInBackground: %b",
157 hasPrimaryCallChanged,
158 canVideoPause,
159 mIsInBackground);
Eric Erfanianccca3152017-02-22 16:32:36 -0800160
161 if (hasPrimaryCallChanged) {
162 onPrimaryCallChanged(call);
163 return;
164 }
165
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700166 if (wasDialing() && canVideoPause && mIsInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800167 // Bring UI to foreground if outgoing request becomes active while UI is in
168 // background.
169 bringToForeground();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700170 } else if (!mWasVideoCall && canVideoPause && mIsInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800171 // Bring UI to foreground if VoLTE call becomes active while UI is in
172 // background.
173 bringToForeground();
174 }
175
176 updatePrimaryCallContext(call);
177 }
178
179 /**
180 * Handles a change to the primary call.
181 *
182 * <p>Reject incoming or hangup dialing call: Where the previous call was an incoming call or a
183 * call in dialing state, resume the new primary call. DialerCall swap: Where the new primary call
184 * is incoming, pause video on the previous primary call.
185 *
186 * @param call The new primary call.
187 */
188 private void onPrimaryCallChanged(DialerCall call) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700189 LogUtil.i(
190 "VideoPauseController.onPrimaryCallChanged",
191 "new call: %s, old call: %s, mIsInBackground: %b",
192 call,
193 mPrimaryCall,
194 mIsInBackground);
Eric Erfanianccca3152017-02-22 16:32:36 -0800195
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700196 if (Objects.equals(call, mPrimaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800197 throw new IllegalStateException();
198 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700199 final boolean canVideoPause = videoCanPause(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800200
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700201 if ((wasIncomingCall() || wasDialing()) && canVideoPause && !mIsInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800202 // Send resume request for the active call, if user rejects incoming call, ends dialing
203 // call, or the call was previously in a paused state and UI is in the foreground.
204 sendRequest(call, true);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700205 } else if (isIncomingCall(call) && videoCanPause(mPrimaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800206 // Send pause request if there is an active video call, and we just received a new
207 // incoming call.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700208 sendRequest(mPrimaryCall, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800209 }
210
211 updatePrimaryCallContext(call);
212 }
213
214 /**
215 * Handles new incoming calls by triggering a change in the primary call.
216 *
217 * @param oldState the old {@link InCallState}.
218 * @param newState the new {@link InCallState}.
219 * @param call the incoming call.
220 */
221 @Override
222 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700223 LogUtil.i(
224 "VideoPauseController.onIncomingCall",
225 "oldState: %s, newState: %s, call: %s",
226 oldState,
227 newState,
228 call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800229
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700230 if (Objects.equals(call, mPrimaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 return;
232 }
233
234 onPrimaryCallChanged(call);
235 }
236
237 /**
238 * Caches a reference to the primary call and stores its previous state.
239 *
240 * @param call The new primary call.
241 */
242 private void updatePrimaryCallContext(DialerCall call) {
243 if (call == null) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700244 mPrimaryCall = null;
245 mPrevCallState = State.INVALID;
246 mWasVideoCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800247 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700248 mPrimaryCall = call;
249 mPrevCallState = call.getState();
250 mWasVideoCall = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800251 }
252 }
253
254 /**
255 * Called when UI goes in/out of the foreground.
256 *
257 * @param showing true if UI is in the foreground, false otherwise.
258 */
259 public void onUiShowing(boolean showing) {
260 if (mInCallPresenter == null) {
261 return;
262 }
263
264 final boolean isInCall = mInCallPresenter.getInCallState() == InCallState.INCALL;
265 if (showing) {
266 onResume(isInCall);
267 } else {
268 onPause(isInCall);
269 }
270 }
271
272 /**
273 * Called when UI is brought to the foreground. Sends a session modification request to resume the
274 * outgoing video.
275 *
276 * @param isInCall {@code true} if we are in an active call. A resume request is only sent to the
277 * video provider if we are in a call.
278 */
279 private void onResume(boolean isInCall) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800280 mIsInBackground = false;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700281 if (isInCall) {
282 sendRequest(mPrimaryCall, true);
Eric Erfanianccca3152017-02-22 16:32:36 -0800283 }
284 }
285
286 /**
287 * Called when UI is sent to the background. Sends a session modification request to pause the
288 * outgoing video.
289 *
290 * @param isInCall {@code true} if we are in an active call. A pause request is only sent to the
291 * video provider if we are in a call.
292 */
293 private void onPause(boolean isInCall) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800294 mIsInBackground = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700295 if (isInCall) {
296 sendRequest(mPrimaryCall, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800297 }
298 }
299
300 private void bringToForeground() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700301 LogUtil.enterBlock("VideoPauseController.bringToForeground");
Eric Erfanianccca3152017-02-22 16:32:36 -0800302 if (mInCallPresenter != null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800303 mInCallPresenter.bringToForeground(false);
304 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700305 LogUtil.e(
306 "VideoPauseController.bringToForeground",
307 "InCallPresenter is null. Cannot bring UI to foreground");
Eric Erfanianccca3152017-02-22 16:32:36 -0800308 }
309 }
310
311 /**
312 * Sends Pause/Resume request.
313 *
314 * @param call DialerCall to be paused/resumed.
315 * @param resume If true resume request will be sent, otherwise pause request.
316 */
317 private void sendRequest(DialerCall call, boolean resume) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700318 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800319 return;
320 }
321
322 if (resume) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700323 call.getVideoTech().unpause();
Eric Erfanianccca3152017-02-22 16:32:36 -0800324 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700325 call.getVideoTech().pause();
Eric Erfanianccca3152017-02-22 16:32:36 -0800326 }
327 }
328
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700329 private static boolean videoCanPause(DialerCall call) {
330 return call != null && call.isVideoCall() && call.getState() == DialerCall.State.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800331 }
332}