/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.incallui.rtt.protocol; import android.support.annotation.NonNull; import com.android.dialer.common.Assert; import com.android.dialer.rtt.RttTranscript; import com.android.dialer.rtt.RttTranscriptMessage; import com.google.common.base.Splitter; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** Message class that holds one RTT chat content. */ public final class RttChatMessage { private static final Splitter SPLITTER = Splitter.on(Constants.BUBBLE_BREAKER); public boolean isRemote; private long timstamp; private final StringBuilder content = new StringBuilder(); private boolean isFinished; public boolean isFinished() { return isFinished; } public void finish() { isFinished = true; } public void unfinish() { isFinished = false; } public void append(String text) { for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c == '\b' && content.length() > 0 && content.charAt(content.length() - 1) != '\b') { content.deleteCharAt(content.length() - 1); } else { content.append(c); } } } public String getContent() { return content.toString(); } /** * Computes delta change of two string. * *

e.g. "hello world" -> "hello" : "\b\b\b\b\b\b" * *

"hello world" -> "hello mom!" : "\b\b\b\b\bmom!" * *

"hello world" -> "hello d" : "\b\b\b\b\bd" * *

"hello world" -> "hello new world" : "\b\b\b\b\bnew world" */ public static String computeChangedString(String oldMessage, String newMesssage) { StringBuilder modify = new StringBuilder(); int indexChangeStart = 0; while (indexChangeStart < oldMessage.length() && indexChangeStart < newMesssage.length() && oldMessage.charAt(indexChangeStart) == newMesssage.charAt(indexChangeStart)) { indexChangeStart++; } for (int i = indexChangeStart; i < oldMessage.length(); i++) { modify.append('\b'); } for (int i = indexChangeStart; i < newMesssage.length(); i++) { modify.append(newMesssage.charAt(i)); } return modify.toString(); } public static RttTranscript getRttTranscriptWithNewRemoteMessage( RttTranscript rttTranscript, @NonNull String text) { List messageList = fromTranscript(rttTranscript); updateRemoteRttChatMessage(messageList, text); return RttTranscript.newBuilder() .setId(rttTranscript.getId()) .setNumber(rttTranscript.getNumber()) .setTimestamp(rttTranscript.getTimestamp()) .addAllMessages(toTranscriptMessageList(messageList)) .build(); } /** Update list of {@code RttChatMessage} based on given remote text. */ public static void updateRemoteRttChatMessage( List messageList, @NonNull String text) { Assert.isNotNull(messageList); Iterator splitText = SPLITTER.split(text).iterator(); while (splitText.hasNext()) { String singleMessageContent = splitText.next(); RttChatMessage message; int index = getLastIndexUnfinishedRemoteMessage(messageList); if (index < 0) { message = new RttChatMessage(); message.append(singleMessageContent); message.isRemote = true; if (splitText.hasNext()) { message.finish(); } if (message.content.length() != 0) { messageList.add(message); } } else { message = messageList.get(index); message.append(singleMessageContent); if (splitText.hasNext()) { message.finish(); } if (message.content.length() == 0) { messageList.remove(index); } } StringBuilder content = message.content; // Delete previous messages. while (content.length() > 0 && content.charAt(0) == '\b') { messageList.remove(message); content.delete(0, 1); int previous = getLastIndexRemoteMessage(messageList); // There are more backspaces than existing characters. if (previous < 0) { while (content.length() > 0 && content.charAt(0) == '\b') { content.deleteCharAt(0); } // Add message if there are still characters after backspaces. if (content.length() > 0) { message = new RttChatMessage(); message.append(content.toString()); message.isRemote = true; if (splitText.hasNext()) { message.finish(); } messageList.add(message); } break; } message = messageList.get(previous); message.unfinish(); message.append(content.toString()); content = message.content; } } if (text.endsWith(Constants.BUBBLE_BREAKER)) { int lastIndexRemoteMessage = getLastIndexRemoteMessage(messageList); messageList.get(lastIndexRemoteMessage).finish(); } } private static int getLastIndexUnfinishedRemoteMessage(List messageList) { int i = messageList.size() - 1; while (i >= 0 && (!messageList.get(i).isRemote || messageList.get(i).isFinished)) { i--; } return i; } public static int getLastIndexRemoteMessage(List messageList) { int i = messageList.size() - 1; while (i >= 0 && !messageList.get(i).isRemote) { i--; } return i; } public static int getLastIndexLocalMessage(List messageList) { int i = messageList.size() - 1; while (i >= 0 && messageList.get(i).isRemote) { i--; } return i; } public static List toTranscriptMessageList( List messageList) { List transcriptMessageList = new ArrayList<>(); for (RttChatMessage message : messageList) { transcriptMessageList.add( RttTranscriptMessage.newBuilder() .setContent(message.getContent()) .setTimestamp(message.timstamp) .setIsRemote(message.isRemote) .setIsFinished(message.isFinished) .build()); } return transcriptMessageList; } public static List fromTranscript(RttTranscript rttTranscript) { List messageList = new ArrayList<>(); if (rttTranscript == null) { return messageList; } for (RttTranscriptMessage message : rttTranscript.getMessagesList()) { RttChatMessage chatMessage = new RttChatMessage(); chatMessage.append(message.getContent()); chatMessage.timstamp = message.getTimestamp(); chatMessage.isRemote = message.getIsRemote(); if (message.getIsFinished()) { chatMessage.finish(); } messageList.add(chatMessage); } return messageList; } public RttChatMessage() { timstamp = System.currentTimeMillis(); } }