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.dialer.simulator.impl;
18 
19 import android.annotation.TargetApi;
20 import android.os.Handler;
21 import android.os.HandlerThread;
22 import android.os.Looper;
23 import android.support.annotation.MainThread;
24 import android.telecom.Connection.RttTextStream;
25 import com.android.dialer.common.Assert;
26 import com.android.dialer.common.LogUtil;
27 import com.android.incallui.rtt.protocol.Constants;
28 import java.io.IOException;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Random;
32 
33 /** Chat bot to generate remote RTT chat messages. */
34 @TargetApi(28)
35 class RttChatBot {
36 
37   interface Callback {
type(String text)38     void type(String text);
39   }
40 
41   private static final int START_SENDING = 1;
42   private static final int SEND_MESSAGE = 2;
43 
44   private static final String[] CANDIDATE_MESSAGES =
45       new String[] {
46         "To RTT or not to RTT, that is the question...",
47         "Making TTY great again!",
48         "I would be more comfortable with real \"Thyme\" chatting."
49             + " I don't know how to end this pun",
50         "お疲れ様でした",
51         "The FCC has mandated that I respond... I will do so begrudgingly",
52         "��������"
53       };
54 
55   private final MessageHandler messageHandler;
56   private final HandlerThread handlerThread;
57 
RttChatBot(RttTextStream rttTextStream)58   RttChatBot(RttTextStream rttTextStream) {
59     handlerThread = new HandlerThread("RttChatBot");
60     handlerThread.start();
61     messageHandler = new MessageHandler(handlerThread.getLooper(), rttTextStream);
62   }
63 
64   @MainThread
start()65   void start() {
66     Assert.isMainThread();
67     LogUtil.enterBlock("RttChatBot.start");
68     messageHandler.sendEmptyMessage(START_SENDING);
69   }
70 
71   @MainThread
stop()72   void stop() {
73     Assert.isMainThread();
74     LogUtil.enterBlock("RttChatBot.stop");
75     if (handlerThread != null && handlerThread.isAlive()) {
76       handlerThread.quit();
77     }
78   }
79 
80   private static class MessageHandler extends Handler {
81     private final RttTextStream rttTextStream;
82     private final Random random = new Random();
83     private final List<String> messageQueue = new ArrayList<>();
84     private int currentTypingPosition = -1;
85     private String currentTypingMessage = null;
86 
MessageHandler(Looper looper, RttTextStream rttTextStream)87     MessageHandler(Looper looper, RttTextStream rttTextStream) {
88       super(looper);
89       this.rttTextStream = rttTextStream;
90     }
91 
92     @Override
handleMessage(android.os.Message msg)93     public void handleMessage(android.os.Message msg) {
94       switch (msg.what) {
95         case START_SENDING:
96           sendMessage(obtainMessage(SEND_MESSAGE, nextTyping()));
97           break;
98         case SEND_MESSAGE:
99           String message = (String) msg.obj;
100           try {
101             rttTextStream.write(message);
102           } catch (IOException e) {
103             LogUtil.e("RttChatBot.MessageHandler", "write message", e);
104           }
105           if (Constants.BUBBLE_BREAKER.equals(message)) {
106             // Wait 1-11s between two messages.
107             sendMessageDelayed(
108                 obtainMessage(SEND_MESSAGE, nextTyping()), 1000 * (1 + random.nextInt(10)));
109           } else {
110             // Wait up to 2s between typing.
111             sendMessageDelayed(obtainMessage(SEND_MESSAGE, nextTyping()), 200 * random.nextInt(10));
112           }
113           break;
114         default: // fall out
115       }
116     }
117 
nextTyping()118     private String nextTyping() {
119       if (currentTypingPosition < 0 || currentTypingMessage == null) {
120         if (messageQueue.isEmpty()) {
121           String text = CANDIDATE_MESSAGES[random.nextInt(CANDIDATE_MESSAGES.length)];
122           messageQueue.add(text);
123         }
124         currentTypingMessage = messageQueue.remove(0);
125         currentTypingPosition = 0;
126       }
127       if (currentTypingPosition < currentTypingMessage.length()) {
128         int size = random.nextInt(currentTypingMessage.length() - currentTypingPosition + 1);
129         String messageToType =
130             currentTypingMessage.substring(currentTypingPosition, currentTypingPosition + size);
131         currentTypingPosition = currentTypingPosition + size;
132         return messageToType;
133       } else {
134         currentTypingPosition = -1;
135         currentTypingMessage = null;
136         return Constants.BUBBLE_BREAKER;
137       }
138     }
139   }
140 }
141