blob: 1a65010f072d23b65840c7df5d9b043511ff9497 [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 {
linyuh183cb712017-12-27 17:02:37 -080035 private static VideoPauseController videoPauseController;
36 private InCallPresenter inCallPresenter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070037
38 /** The current call, if applicable. */
linyuh183cb712017-12-27 17:02:37 -080039 private DialerCall primaryCall = null;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070040
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 */
linyuh183cb712017-12-27 17:02:37 -080046 private int prevCallState = State.INVALID;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070047
linyuh183cb712017-12-27 17:02:37 -080048 private boolean wasVideoCall = false;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070049
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 */
linyuh183cb712017-12-27 17:02:37 -080054 private boolean isInBackground = false;
Eric Erfanianccca3152017-02-22 16:32:36 -080055
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() {
linyuh183cb712017-12-27 17:02:37 -080063 if (videoPauseController == null) {
64 videoPauseController = new VideoPauseController();
Eric Erfanianccca3152017-02-22 16:32:36 -080065 }
linyuh183cb712017-12-27 17:02:37 -080066 return videoPauseController;
Eric Erfanianccca3152017-02-22 16:32:36 -080067 }
68
Eric Erfanianccca3152017-02-22 16:32:36 -080069 /**
70 * Determines if a call is in incoming/waiting state.
71 *
72 * @param call The call.
73 * @return {@code true} if the call is in incoming or waiting state, {@code false} otherwise.
74 */
75 private static boolean isIncomingCall(DialerCall call) {
76 return call != null
77 && (call.getState() == DialerCall.State.CALL_WAITING
78 || call.getState() == DialerCall.State.INCOMING);
79 }
80
81 /**
82 * Determines if a call is dialing.
83 *
Eric Erfanianccca3152017-02-22 16:32:36 -080084 * @return {@code true} if the call is dialing, {@code false} otherwise.
85 */
Eric Erfaniand5e47f62017-03-15 14:41:07 -070086 private boolean wasDialing() {
linyuh183cb712017-12-27 17:02:37 -080087 return DialerCall.State.isDialing(prevCallState);
Eric Erfanianccca3152017-02-22 16:32:36 -080088 }
89
90 /**
91 * Configures the {@link VideoPauseController} to listen to call events. Configured via the {@link
92 * com.android.incallui.InCallPresenter}.
93 *
94 * @param inCallPresenter The {@link com.android.incallui.InCallPresenter}.
95 */
96 public void setUp(@NonNull InCallPresenter inCallPresenter) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -070097 LogUtil.enterBlock("VideoPauseController.setUp");
linyuh183cb712017-12-27 17:02:37 -080098 this.inCallPresenter = Assert.isNotNull(inCallPresenter);
99 this.inCallPresenter.addListener(this);
100 this.inCallPresenter.addIncomingCallListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800101 }
102
103 /**
104 * Cleans up the {@link VideoPauseController} by removing all listeners and clearing its internal
105 * state. Called from {@link com.android.incallui.InCallPresenter}.
106 */
107 public void tearDown() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700108 LogUtil.enterBlock("VideoPauseController.tearDown");
linyuh183cb712017-12-27 17:02:37 -0800109 inCallPresenter.removeListener(this);
110 inCallPresenter.removeIncomingCallListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800111 clear();
112 }
113
114 /** Clears the internal state for the {@link VideoPauseController}. */
115 private void clear() {
linyuh183cb712017-12-27 17:02:37 -0800116 inCallPresenter = null;
117 primaryCall = null;
118 prevCallState = State.INVALID;
119 wasVideoCall = false;
120 isInBackground = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800121 }
122
123 /**
124 * Handles changes in the {@link InCallState}. Triggers pause and resumption of video for the
125 * current foreground call.
126 *
127 * @param oldState The previous {@link InCallState}.
128 * @param newState The current {@link InCallState}.
129 * @param callList List of current call.
130 */
131 @Override
132 public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800133 DialerCall call;
134 if (newState == InCallState.INCOMING) {
135 call = callList.getIncomingCall();
136 } else if (newState == InCallState.WAITING_FOR_ACCOUNT) {
137 call = callList.getWaitingForAccountCall();
138 } else if (newState == InCallState.PENDING_OUTGOING) {
139 call = callList.getPendingOutgoingCall();
140 } else if (newState == InCallState.OUTGOING) {
141 call = callList.getOutgoingCall();
142 } else {
143 call = callList.getActiveCall();
144 }
145
linyuh183cb712017-12-27 17:02:37 -0800146 boolean hasPrimaryCallChanged = !Objects.equals(call, primaryCall);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700147 boolean canVideoPause = videoCanPause(call);
148
149 LogUtil.i(
150 "VideoPauseController.onStateChange",
151 "hasPrimaryCallChanged: %b, videoCanPause: %b, isInBackground: %b",
152 hasPrimaryCallChanged,
153 canVideoPause,
linyuh183cb712017-12-27 17:02:37 -0800154 isInBackground);
Eric Erfanianccca3152017-02-22 16:32:36 -0800155
156 if (hasPrimaryCallChanged) {
157 onPrimaryCallChanged(call);
158 return;
159 }
160
linyuh183cb712017-12-27 17:02:37 -0800161 if (wasDialing() && canVideoPause && isInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800162 // Bring UI to foreground if outgoing request becomes active while UI is in
163 // background.
164 bringToForeground();
linyuh183cb712017-12-27 17:02:37 -0800165 } else if (!wasVideoCall && canVideoPause && isInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800166 // Bring UI to foreground if VoLTE call becomes active while UI is in
167 // background.
168 bringToForeground();
169 }
170
171 updatePrimaryCallContext(call);
172 }
173
174 /**
175 * Handles a change to the primary call.
176 *
177 * <p>Reject incoming or hangup dialing call: Where the previous call was an incoming call or a
178 * call in dialing state, resume the new primary call. DialerCall swap: Where the new primary call
179 * is incoming, pause video on the previous primary call.
180 *
181 * @param call The new primary call.
182 */
183 private void onPrimaryCallChanged(DialerCall call) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700184 LogUtil.i(
185 "VideoPauseController.onPrimaryCallChanged",
186 "new call: %s, old call: %s, mIsInBackground: %b",
187 call,
linyuh183cb712017-12-27 17:02:37 -0800188 primaryCall,
189 isInBackground);
Eric Erfanianccca3152017-02-22 16:32:36 -0800190
linyuh183cb712017-12-27 17:02:37 -0800191 if (Objects.equals(call, primaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800192 throw new IllegalStateException();
193 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700194 final boolean canVideoPause = videoCanPause(call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800195
linyuh183cb712017-12-27 17:02:37 -0800196 if (canVideoPause && !isInBackground) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800197 // Send resume request for the active call, if user rejects incoming call, ends dialing
198 // call, or the call was previously in a paused state and UI is in the foreground.
199 sendRequest(call, true);
linyuh183cb712017-12-27 17:02:37 -0800200 } else if (isIncomingCall(call) && videoCanPause(primaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800201 // Send pause request if there is an active video call, and we just received a new
202 // incoming call.
linyuh183cb712017-12-27 17:02:37 -0800203 sendRequest(primaryCall, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 }
205
206 updatePrimaryCallContext(call);
207 }
208
209 /**
210 * Handles new incoming calls by triggering a change in the primary call.
211 *
212 * @param oldState the old {@link InCallState}.
213 * @param newState the new {@link InCallState}.
214 * @param call the incoming call.
215 */
216 @Override
217 public void onIncomingCall(InCallState oldState, InCallState newState, DialerCall call) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700218 LogUtil.i(
219 "VideoPauseController.onIncomingCall",
220 "oldState: %s, newState: %s, call: %s",
221 oldState,
222 newState,
223 call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800224
linyuh183cb712017-12-27 17:02:37 -0800225 if (Objects.equals(call, primaryCall)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800226 return;
227 }
228
229 onPrimaryCallChanged(call);
230 }
231
232 /**
233 * Caches a reference to the primary call and stores its previous state.
234 *
235 * @param call The new primary call.
236 */
237 private void updatePrimaryCallContext(DialerCall call) {
238 if (call == null) {
linyuh183cb712017-12-27 17:02:37 -0800239 primaryCall = null;
240 prevCallState = State.INVALID;
241 wasVideoCall = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800242 } else {
linyuh183cb712017-12-27 17:02:37 -0800243 primaryCall = call;
244 prevCallState = call.getState();
245 wasVideoCall = call.isVideoCall();
Eric Erfanianccca3152017-02-22 16:32:36 -0800246 }
247 }
248
249 /**
250 * Called when UI goes in/out of the foreground.
251 *
252 * @param showing true if UI is in the foreground, false otherwise.
253 */
254 public void onUiShowing(boolean showing) {
linyuh183cb712017-12-27 17:02:37 -0800255 if (inCallPresenter == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800256 return;
257 }
258
linyuh183cb712017-12-27 17:02:37 -0800259 final boolean isInCall = inCallPresenter.getInCallState() == InCallState.INCALL;
Eric Erfanianccca3152017-02-22 16:32:36 -0800260 if (showing) {
261 onResume(isInCall);
262 } else {
263 onPause(isInCall);
264 }
265 }
266
267 /**
268 * Called when UI is brought to the foreground. Sends a session modification request to resume the
269 * outgoing video.
270 *
271 * @param isInCall {@code true} if we are in an active call. A resume request is only sent to the
272 * video provider if we are in a call.
273 */
274 private void onResume(boolean isInCall) {
linyuh183cb712017-12-27 17:02:37 -0800275 isInBackground = false;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700276 if (isInCall) {
linyuh183cb712017-12-27 17:02:37 -0800277 sendRequest(primaryCall, true);
Eric Erfanianccca3152017-02-22 16:32:36 -0800278 }
279 }
280
281 /**
282 * Called when UI is sent to the background. Sends a session modification request to pause the
283 * outgoing video.
284 *
285 * @param isInCall {@code true} if we are in an active call. A pause request is only sent to the
286 * video provider if we are in a call.
287 */
288 private void onPause(boolean isInCall) {
linyuh183cb712017-12-27 17:02:37 -0800289 isInBackground = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700290 if (isInCall) {
linyuh183cb712017-12-27 17:02:37 -0800291 sendRequest(primaryCall, false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 }
293 }
294
295 private void bringToForeground() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700296 LogUtil.enterBlock("VideoPauseController.bringToForeground");
linyuh183cb712017-12-27 17:02:37 -0800297 if (inCallPresenter != null) {
298 inCallPresenter.bringToForeground(false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800299 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700300 LogUtil.e(
301 "VideoPauseController.bringToForeground",
302 "InCallPresenter is null. Cannot bring UI to foreground");
Eric Erfanianccca3152017-02-22 16:32:36 -0800303 }
304 }
305
306 /**
307 * Sends Pause/Resume request.
308 *
309 * @param call DialerCall to be paused/resumed.
310 * @param resume If true resume request will be sent, otherwise pause request.
311 */
312 private void sendRequest(DialerCall call, boolean resume) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700313 if (call == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800314 return;
315 }
316
317 if (resume) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700318 call.getVideoTech().unpause();
Eric Erfanianccca3152017-02-22 16:32:36 -0800319 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700320 call.getVideoTech().pause();
Eric Erfanianccca3152017-02-22 16:32:36 -0800321 }
322 }
323
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700324 private static boolean videoCanPause(DialerCall call) {
325 return call != null && call.isVideoCall() && call.getState() == DialerCall.State.ACTIVE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800326 }
327}