1 /*
2  * Copyright (C) 2018 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 
17 package com.android.incallui;
18 
19 import android.annotation.TargetApi;
20 import android.os.Handler;
21 import android.os.HandlerThread;
22 import android.os.Looper;
23 import android.telecom.Call.RttCall;
24 import com.android.dialer.common.LogUtil;
25 import com.android.dialer.common.concurrent.ThreadUtil;
26 import com.android.dialer.rtt.RttTranscript;
27 import com.android.incallui.InCallPresenter.InCallState;
28 import com.android.incallui.InCallPresenter.InCallStateListener;
29 import com.android.incallui.call.CallList;
30 import com.android.incallui.call.DialerCall;
31 import com.android.incallui.rtt.protocol.RttCallScreen;
32 import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
33 import java.io.IOException;
34 
35 /**
36  * Logic related to the {@link RttCallScreen} and for managing changes to the RTT calling surfaces
37  * based on other user interface events and incoming events.
38  */
39 @TargetApi(28)
40 public class RttCallPresenter implements RttCallScreenDelegate, InCallStateListener {
41 
42   private RttCallScreen rttCallScreen;
43   private RttCall rttCall;
44   private HandlerThread handlerThread;
45   private RemoteMessageHandler remoteMessageHandler;
46 
47   @Override
initRttCallScreenDelegate(RttCallScreen rttCallScreen)48   public void initRttCallScreenDelegate(RttCallScreen rttCallScreen) {
49     this.rttCallScreen = rttCallScreen;
50   }
51 
52   @Override
onLocalMessage(String message)53   public void onLocalMessage(String message) {
54     if (rttCall == null) {
55       LogUtil.w("RttCallPresenter.onLocalMessage", "Rtt Call is not started yet");
56       return;
57     }
58     remoteMessageHandler.writeMessage(message);
59   }
60 
61   @Override
onRttCallScreenUiReady()62   public void onRttCallScreenUiReady() {
63     LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiReady");
64     InCallPresenter.getInstance().addListener(this);
65     startListenOnRemoteMessage();
66     DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
67     if (call != null) {
68       rttCallScreen.onRestoreRttChat(call.getRttTranscript());
69     }
70   }
71 
72   @Override
onSaveRttTranscript()73   public void onSaveRttTranscript() {
74     LogUtil.enterBlock("RttCallPresenter.onSaveRttTranscript");
75     DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
76     if (call != null) {
77       saveTranscript(call);
78     }
79   }
80 
81   @Override
onRttCallScreenUiUnready()82   public void onRttCallScreenUiUnready() {
83     LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiUnready");
84     InCallPresenter.getInstance().removeListener(this);
85     stopListenOnRemoteMessage();
86     onSaveRttTranscript();
87   }
88 
saveTranscript(DialerCall dialerCall)89   private void saveTranscript(DialerCall dialerCall) {
90     LogUtil.enterBlock("RttCallPresenter.saveTranscript");
91     RttTranscript.Builder builder = RttTranscript.newBuilder();
92     builder
93 
94         .setId(String.valueOf(dialerCall.getCreationTimeMillis()))
95 
96         .setTimestamp(dialerCall.getCreationTimeMillis())
97         .setNumber(dialerCall.getNumber())
98         .addAllMessages(rttCallScreen.getRttTranscriptMessageList());
99     dialerCall.setRttTranscript(builder.build());
100   }
101 
102   @Override
onStateChange(InCallState oldState, InCallState newState, CallList callList)103   public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
104     LogUtil.enterBlock("RttCallPresenter.onStateChange");
105     if (newState == InCallState.INCALL) {
106       startListenOnRemoteMessage();
107     }
108   }
109 
startListenOnRemoteMessage()110   private void startListenOnRemoteMessage() {
111     DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId());
112     if (call == null) {
113       LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call does not exist");
114       return;
115     }
116     rttCall = call.getRttCall();
117     if (rttCall == null) {
118       LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "RTT Call is not started yet");
119       return;
120     }
121     if (handlerThread != null && handlerThread.isAlive()) {
122       LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "already running");
123       return;
124     }
125     handlerThread = new HandlerThread("RttCallRemoteMessageHandler");
126     handlerThread.start();
127     remoteMessageHandler =
128         new RemoteMessageHandler(handlerThread.getLooper(), rttCall, rttCallScreen);
129     remoteMessageHandler.start();
130   }
131 
stopListenOnRemoteMessage()132   private void stopListenOnRemoteMessage() {
133     if (handlerThread != null && handlerThread.isAlive()) {
134       handlerThread.quit();
135     }
136   }
137 
138   private static class RemoteMessageHandler extends Handler {
139     private static final int START = 1;
140     private static final int READ_MESSAGE = 2;
141     private static final int WRITE_MESSAGE = 3;
142 
143     private final RttCall rttCall;
144     private final RttCallScreen rttCallScreen;
145 
RemoteMessageHandler(Looper looper, RttCall rttCall, RttCallScreen rttCallScreen)146     RemoteMessageHandler(Looper looper, RttCall rttCall, RttCallScreen rttCallScreen) {
147       super(looper);
148       this.rttCall = rttCall;
149       this.rttCallScreen = rttCallScreen;
150     }
151 
152     @Override
handleMessage(android.os.Message msg)153     public void handleMessage(android.os.Message msg) {
154       switch (msg.what) {
155         case START:
156           sendEmptyMessage(READ_MESSAGE);
157           break;
158         case READ_MESSAGE:
159           try {
160             final String message = rttCall.readImmediately();
161             if (message != null) {
162               ThreadUtil.postOnUiThread(() -> rttCallScreen.onRemoteMessage(message));
163             }
164           } catch (IOException e) {
165             LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "read message", e);
166           }
167           sendEmptyMessageDelayed(READ_MESSAGE, 200);
168           break;
169         case WRITE_MESSAGE:
170           try {
171             rttCall.write((String) msg.obj);
172           } catch (IOException e) {
173             LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "write message", e);
174           }
175           break;
176         default: // fall out
177       }
178     }
179 
start()180     void start() {
181       sendEmptyMessage(START);
182     }
183 
writeMessage(String message)184     void writeMessage(String message) {
185       sendMessage(obtainMessage(WRITE_MESSAGE, message));
186     }
187   }
188 }
189