1/*
2 * Copyright (C) 2016 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.content.Context;
20import android.os.SystemClock;
21import android.support.annotation.FloatRange;
22import android.support.annotation.NonNull;
23import android.support.v4.os.UserManagerCompat;
24import android.telecom.VideoProfile;
25import com.android.dialer.common.Assert;
26import com.android.dialer.common.LogUtil;
27import com.android.dialer.common.concurrent.ThreadUtil;
28import com.android.dialer.logging.DialerImpression;
29import com.android.dialer.logging.Logger;
30import com.android.incallui.answer.protocol.AnswerScreen;
31import com.android.incallui.answer.protocol.AnswerScreenDelegate;
32import com.android.incallui.answerproximitysensor.AnswerProximitySensor;
33import com.android.incallui.answerproximitysensor.PseudoScreenState;
34import com.android.incallui.call.CallList;
35import com.android.incallui.call.DialerCall;
36import com.android.incallui.call.DialerCallListener;
37
38/** Manages changes for an incoming call screen. */
39public class AnswerScreenPresenter
40    implements AnswerScreenDelegate, DialerCall.CannedTextResponsesLoadedListener {
41  private static final int ACCEPT_REJECT_CALL_TIME_OUT_IN_MILLIS = 5000;
42
43  @NonNull private final Context context;
44  @NonNull private final AnswerScreen answerScreen;
45  @NonNull private final DialerCall call;
46  private long actionPerformedTimeMillis;
47
48  AnswerScreenPresenter(
49      @NonNull Context context, @NonNull AnswerScreen answerScreen, @NonNull DialerCall call) {
50    LogUtil.i("AnswerScreenPresenter.constructor", null);
51    this.context = Assert.isNotNull(context);
52    this.answerScreen = Assert.isNotNull(answerScreen);
53    this.call = Assert.isNotNull(call);
54    if (isSmsResponseAllowed(call)) {
55      answerScreen.setTextResponses(call.getCannedSmsResponses());
56    }
57    call.addCannedTextResponsesLoadedListener(this);
58
59    PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
60    if (AnswerProximitySensor.shouldUse(context, call)) {
61      new AnswerProximitySensor(context, call, pseudoScreenState);
62    } else {
63      pseudoScreenState.setOn(true);
64    }
65  }
66
67  @Override
68  public boolean isActionTimeout() {
69    return actionPerformedTimeMillis != 0
70        && SystemClock.elapsedRealtime() - actionPerformedTimeMillis
71            >= ACCEPT_REJECT_CALL_TIME_OUT_IN_MILLIS;
72  }
73
74  @Override
75  public void onAnswerScreenUnready() {
76    call.removeCannedTextResponsesLoadedListener(this);
77  }
78
79  @Override
80  public void onDismissDialog() {
81    InCallPresenter.getInstance().onDismissDialog();
82  }
83
84  @Override
85  public void onRejectCallWithMessage(String message) {
86    call.reject(true /* rejectWithMessage */, message);
87    onDismissDialog();
88    addTimeoutCheck();
89  }
90
91  @Override
92  public void onAnswer(boolean answerVideoAsAudio) {
93    if (answerScreen.isVideoUpgradeRequest()) {
94      if (answerVideoAsAudio) {
95        Logger.get(context)
96            .logCallImpression(
97                DialerImpression.Type.VIDEO_CALL_REQUEST_ACCEPTED_AS_AUDIO,
98                call.getUniqueCallId(),
99                call.getTimeAddedMs());
100        call.getVideoTech().acceptVideoRequestAsAudio();
101      } else {
102        Logger.get(context)
103            .logCallImpression(
104                DialerImpression.Type.VIDEO_CALL_REQUEST_ACCEPTED,
105                call.getUniqueCallId(),
106                call.getTimeAddedMs());
107        call.getVideoTech().acceptVideoRequest();
108      }
109    } else {
110      if (answerVideoAsAudio) {
111        call.answer(VideoProfile.STATE_AUDIO_ONLY);
112      } else {
113        call.answer();
114      }
115    }
116    addTimeoutCheck();
117  }
118
119  @Override
120  public void onReject() {
121    if (answerScreen.isVideoUpgradeRequest()) {
122      Logger.get(context)
123          .logCallImpression(
124              DialerImpression.Type.VIDEO_CALL_REQUEST_DECLINED,
125              call.getUniqueCallId(),
126              call.getTimeAddedMs());
127      call.getVideoTech().declineVideoRequest();
128    } else {
129      call.reject(false /* rejectWithMessage */, null);
130    }
131    addTimeoutCheck();
132  }
133
134  @Override
135  public void onAnswerAndReleaseCall() {
136    LogUtil.enterBlock("AnswerScreenPresenter.onAnswerAndReleaseCall");
137    DialerCall activeCall = CallList.getInstance().getActiveCall();
138    if (activeCall == null) {
139      LogUtil.i("AnswerScreenPresenter.onAnswerAndReleaseCall", "activeCall == null");
140      onAnswer(false);
141    } else {
142      activeCall.setReleasedByAnsweringSecondCall(true);
143      activeCall.addListener(new AnswerOnDisconnected(activeCall));
144      activeCall.disconnect();
145    }
146    addTimeoutCheck();
147  }
148
149  @Override
150  public void onAnswerAndReleaseButtonDisabled() {
151    DialerCall activeCall = CallList.getInstance().getActiveCall();
152    if (activeCall != null) {
153      activeCall.increaseSecondCallWithoutAnswerAndReleasedButtonTimes();
154    }
155  }
156
157  @Override
158  public void onAnswerAndReleaseButtonEnabled() {
159    DialerCall activeCall = CallList.getInstance().getActiveCall();
160    if (activeCall != null) {
161      activeCall.increaseAnswerAndReleaseButtonDisplayedTimes();
162    }
163  }
164
165  @Override
166  public void onCannedTextResponsesLoaded(DialerCall call) {
167    if (isSmsResponseAllowed(call)) {
168      answerScreen.setTextResponses(call.getCannedSmsResponses());
169    }
170  }
171
172  @Override
173  public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
174    InCallActivity activity = (InCallActivity) answerScreen.getAnswerScreenFragment().getActivity();
175    if (activity != null) {
176      activity.updateWindowBackgroundColor(progress);
177    }
178  }
179
180  private class AnswerOnDisconnected implements DialerCallListener {
181
182    private final DialerCall disconnectingCall;
183
184    AnswerOnDisconnected(DialerCall disconnectingCall) {
185      this.disconnectingCall = disconnectingCall;
186    }
187
188    @Override
189    public void onDialerCallDisconnect() {
190      LogUtil.i(
191          "AnswerScreenPresenter.AnswerOnDisconnected", "call disconnected, answering new call");
192      call.answer();
193      disconnectingCall.removeListener(this);
194    }
195
196    @Override
197    public void onDialerCallUpdate() {}
198
199    @Override
200    public void onDialerCallChildNumberChange() {}
201
202    @Override
203    public void onDialerCallLastForwardedNumberChange() {}
204
205    @Override
206    public void onDialerCallUpgradeToVideo() {}
207
208    @Override
209    public void onDialerCallSessionModificationStateChange() {}
210
211    @Override
212    public void onWiFiToLteHandover() {}
213
214    @Override
215    public void onHandoverToWifiFailure() {}
216
217    @Override
218    public void onInternationalCallOnWifi() {}
219
220    @Override
221    public void onEnrichedCallSessionUpdate() {}
222  }
223
224  private boolean isSmsResponseAllowed(DialerCall call) {
225    return UserManagerCompat.isUserUnlocked(context)
226        && call.can(android.telecom.Call.Details.CAPABILITY_RESPOND_VIA_TEXT);
227  }
228
229  private void addTimeoutCheck() {
230    actionPerformedTimeMillis = SystemClock.elapsedRealtime();
231    if (answerScreen.getAnswerScreenFragment().isVisible()) {
232      ThreadUtil.postDelayedOnUiThread(
233          () -> {
234            if (!answerScreen.getAnswerScreenFragment().isVisible()) {
235              LogUtil.d(
236                  "AnswerScreenPresenter.addTimeoutCheck",
237                  "accept/reject call timed out, do nothing");
238              return;
239            }
240            LogUtil.i("AnswerScreenPresenter.addTimeoutCheck", "accept/reject call timed out");
241            // Force re-evaluate which fragment to show.
242            InCallPresenter.getInstance().refreshUi();
243          },
244          ACCEPT_REJECT_CALL_TIME_OUT_IN_MILLIS);
245    }
246  }
247}
248