From 153af2febd64f989f5e67c51c0653489e3339a1a Mon Sep 17 00:00:00 2001 From: wangqi Date: Thu, 15 Feb 2018 16:21:49 -0800 Subject: Implement read/write text stream to RttCall. This change also: 1. Add simulator support of RTT request during call (always accept at this moment, will add random accept/decline in the future) 2. Fix bugs of putting RTT call in background and back to call Bug: 67596257 Test: Simulator PiperOrigin-RevId: 185920527 Change-Id: I51016fa6cf1ccc8a5a21335f9dacf286ae393706 --- .../android/dialer/simulator/impl/RttChatBot.java | 1 - .../dialer/simulator/impl/SimulatorConnection.java | 29 ++++- .../dialer/simulator/impl/SimulatorRttCall.java | 12 +- .../simulator/impl/SimulatorSimCallManager.java | 2 +- .../dialer/simulator/impl/SimulatorVoiceCall.java | 14 ++- java/com/android/incallui/InCallActivity.java | 3 + java/com/android/incallui/RttCallPresenter.java | 131 +++++++++++++++++++-- java/com/android/incallui/call/DialerCall.java | 9 +- .../android/incallui/rtt/impl/RttChatAdapter.java | 12 +- .../android/incallui/rtt/impl/RttChatFragment.java | 21 +++- .../incallui/rtt/protocol/RttCallScreen.java | 2 + .../rtt/protocol/RttCallScreenDelegate.java | 6 +- 12 files changed, 218 insertions(+), 24 deletions(-) diff --git a/java/com/android/dialer/simulator/impl/RttChatBot.java b/java/com/android/dialer/simulator/impl/RttChatBot.java index 5a7769f3e..b2860e387 100644 --- a/java/com/android/dialer/simulator/impl/RttChatBot.java +++ b/java/com/android/dialer/simulator/impl/RttChatBot.java @@ -97,7 +97,6 @@ public class RttChatBot { break; case SEND_MESSAGE: String message = (String) msg.obj; - LogUtil.w("test", "type: %s, to stream: %s", message, rttTextStream); try { rttTextStream.write(message); } catch (IOException e) { diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java index c832a5051..3aa3296ea 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java @@ -16,9 +16,12 @@ package com.android.dialer.simulator.impl; +import android.annotation.TargetApi; import android.content.Context; import android.support.annotation.NonNull; +import android.support.v4.os.BuildCompat; import android.telecom.Connection; +import android.telecom.Connection.RttTextStream; import android.telecom.ConnectionRequest; import android.telecom.VideoProfile; import com.android.dialer.common.Assert; @@ -31,11 +34,14 @@ import java.util.ArrayList; import java.util.List; /** Represents a single phone call on the device. */ +@TargetApi(28) public final class SimulatorConnection extends Connection { private final List listeners = new ArrayList<>(); private final List events = new ArrayList<>(); private final SimulatorConnectionsBank simulatorConnectionsBank; private int currentState = STATE_NEW; + private RttTextStream rttTextStream; + private RttChatBot rttChatBot; SimulatorConnection(@NonNull Context context, @NonNull ConnectionRequest request) { Assert.isNotNull(context); @@ -54,6 +60,9 @@ public final class SimulatorConnection extends Connection { getConnectionCapabilities() | CAPABILITY_SEPARATE_FROM_CONFERENCE); } } + if (BuildCompat.isAtLeastP()) { + rttTextStream = request.getRttTextStream(); + } setVideoProvider(new SimulatorVideoProvider(context, this)); simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank(); } @@ -66,6 +75,10 @@ public final class SimulatorConnection extends Connection { listeners.remove(Assert.isNotNull(listener)); } + RttTextStream getRttTextStream() { + return rttTextStream; + } + @NonNull public List getEvents() { return events; @@ -101,6 +114,11 @@ public final class SimulatorConnection extends Connection { LogUtil.enterBlock("SimulatorConnection.onDisconnect"); simulatorConnectionsBank.remove(this); onEvent(new Event(Event.DISCONNECT)); + rttTextStream = null; + if (rttChatBot != null) { + rttChatBot.stop(); + rttChatBot = null; + } } @Override @@ -124,12 +142,21 @@ public final class SimulatorConnection extends Connection { @Override public void onStartRtt(@NonNull RttTextStream rttTextStream) { LogUtil.enterBlock("SimulatorConnection.onStartRtt"); + if (this.rttTextStream != null || rttChatBot != null) { + LogUtil.e("SimulatorConnection.onStartRtt", "rttTextStream or rttChatBot is not null!"); + } + this.rttTextStream = rttTextStream; + rttChatBot = new RttChatBot(rttTextStream); + rttChatBot.start(); onEvent(new Event(Event.START_RTT)); } @Override public void onStopRtt() { LogUtil.enterBlock("SimulatorConnection.onStopRtt"); + rttChatBot.stop(); + rttChatBot = null; + rttTextStream = null; onEvent(new Event(Event.STOP_RTT)); } @@ -159,6 +186,4 @@ public final class SimulatorConnection extends Connection { public interface Listener { void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event); } - - } diff --git a/java/com/android/dialer/simulator/impl/SimulatorRttCall.java b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java index 7b0066719..352b9e4ef 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorRttCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java @@ -34,6 +34,7 @@ final class SimulatorRttCall @NonNull private final Context context; @Nullable private String connectionTag; + private RttChatBot rttChatBot; static ActionProvider getActionProvider(@NonNull Context context) { return new SimulatorSubMenu(context) @@ -112,24 +113,29 @@ final class SimulatorRttCall switch (event.type) { case Event.NONE: throw Assert.createIllegalStateFailException(); - case Event.ANSWER: - connection.setActive(); - break; case Event.REJECT: connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED)); break; case Event.HOLD: connection.setOnHold(); break; + case Event.ANSWER: case Event.UNHOLD: connection.setActive(); break; case Event.DISCONNECT: + rttChatBot.stop(); connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); break; case Event.SESSION_MODIFY_REQUEST: ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000); break; + case Event.STATE_CHANGE: + if (Connection.stateToString(Connection.STATE_ACTIVE).equals(event.data2)) { + rttChatBot = new RttChatBot(connection.getRttTextStream()); + rttChatBot.start(); + } + break; default: LogUtil.i("SimulatorRttCall.onEvent", "unexpected event: " + event.type); break; diff --git a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java index d51e06816..c56afb21f 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java +++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java @@ -115,7 +115,7 @@ public class SimulatorSimCallManager { TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, callType == CALL_TYPE_VIDEO ? getVideoProviderHandle(context) - : getSystemPhoneAccountHandle(context)); + : getSimCallManagerHandle(context)); if (callType == CALL_TYPE_RTT) { outgoingCallExtras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true); } diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java index d4c7ee458..e59cddd51 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java @@ -22,6 +22,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.telecom.Connection; +import android.telecom.Connection.RttModifyStatus; import android.telecom.DisconnectCause; import android.view.ActionProvider; import com.android.dialer.common.Assert; @@ -223,15 +224,13 @@ final class SimulatorVoiceCall switch (event.type) { case Event.NONE: throw Assert.createIllegalStateFailException(); - case Event.ANSWER: - connection.setActive(); - break; case Event.REJECT: connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED)); break; case Event.HOLD: connection.setOnHold(); break; + case Event.ANSWER: case Event.UNHOLD: connection.setActive(); break; @@ -244,6 +243,15 @@ final class SimulatorVoiceCall case Event.SESSION_MODIFY_REQUEST: ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000); break; + case Event.START_RTT: + // TODO(wangqi): Add random accept/decline. + boolean accept = true; + if (accept) { + connection.sendRttInitiationSuccess(); + } else { + connection.sendRttInitiationFailure(RttModifyStatus.SESSION_MODIFY_REQUEST_FAIL); + } + break; default: LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type); break; diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java index 67f5cfe4f..3fc7f6c76 100644 --- a/java/com/android/incallui/InCallActivity.java +++ b/java/com/android/incallui/InCallActivity.java @@ -182,6 +182,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN); didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN); didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN); + didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN); } setWindowFlags(); @@ -387,6 +388,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen); out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen); out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen); + out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen); super.onSaveInstanceState(out); isVisible = false; @@ -1593,6 +1595,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen"; static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen"; static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen"; + static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen"; } /** Request codes for pending intents. */ diff --git a/java/com/android/incallui/RttCallPresenter.java b/java/com/android/incallui/RttCallPresenter.java index b90d56b36..939c9d00b 100644 --- a/java/com/android/incallui/RttCallPresenter.java +++ b/java/com/android/incallui/RttCallPresenter.java @@ -16,28 +16,145 @@ package com.android.incallui; -import android.content.Context; +import android.annotation.TargetApi; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.telecom.Call.RttCall; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.incallui.InCallPresenter.InCallState; +import com.android.incallui.InCallPresenter.InCallStateListener; +import com.android.incallui.call.CallList; +import com.android.incallui.call.DialerCall; import com.android.incallui.rtt.protocol.RttCallScreen; import com.android.incallui.rtt.protocol.RttCallScreenDelegate; +import java.io.IOException; /** * Logic related to the {@link RttCallScreen} and for managing changes to the RTT calling surfaces * based on other user interface events and incoming events. */ -public class RttCallPresenter implements RttCallScreenDelegate { +@TargetApi(28) +public class RttCallPresenter implements RttCallScreenDelegate, InCallStateListener { - private Context appContext; private RttCallScreen rttCallScreen; + private RttCall rttCall; + private HandlerThread handlerThread; + private RemoteMessageHandler remoteMessageHandler; @Override - public void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen) { - this.appContext = context.getApplicationContext(); + public void initRttCallScreenDelegate(RttCallScreen rttCallScreen) { this.rttCallScreen = rttCallScreen; } @Override - public void onRttCallScreenUiReady() {} + public void onLocalMessage(String message) { + if (rttCall == null) { + LogUtil.w("RttCallPresenter.onLocalMessage", "Rtt Call is not started yet"); + return; + } + remoteMessageHandler.writeMessage(message); + } + + @Override + public void onRttCallScreenUiReady() { + LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiReady"); + InCallPresenter.getInstance().addListener(this); + startListenOnRemoteMessage(); + } + + @Override + public void onRttCallScreenUiUnready() { + LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiUnready"); + InCallPresenter.getInstance().removeListener(this); + stopListenOnRemoteMessage(); + } @Override - public void onRttCallScreenUiUnready() {} + public void onStateChange(InCallState oldState, InCallState newState, CallList callList) { + LogUtil.enterBlock("RttCallPresenter.onStateChange"); + if (newState == InCallState.INCALL) { + startListenOnRemoteMessage(); + } + } + + private void startListenOnRemoteMessage() { + DialerCall call = CallList.getInstance().getActiveCall(); + if (call == null) { + LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call is active yet"); + return; + } + rttCall = call.getRttCall(); + if (rttCall == null) { + LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "RTT Call is not started yet"); + return; + } + if (handlerThread != null && handlerThread.isAlive()) { + LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "already running"); + return; + } + handlerThread = new HandlerThread("RttCallRemoteMessageHandler"); + handlerThread.start(); + remoteMessageHandler = + new RemoteMessageHandler(handlerThread.getLooper(), rttCall, rttCallScreen); + remoteMessageHandler.start(); + } + + private void stopListenOnRemoteMessage() { + if (handlerThread != null && handlerThread.isAlive()) { + handlerThread.quit(); + } + } + + private static class RemoteMessageHandler extends Handler { + private static final int START = 1; + private static final int READ_MESSAGE = 2; + private static final int WRITE_MESSAGE = 3; + + private final RttCall rttCall; + private final RttCallScreen rttCallScreen; + + RemoteMessageHandler(Looper looper, RttCall rttCall, RttCallScreen rttCallScreen) { + super(looper); + this.rttCall = rttCall; + this.rttCallScreen = rttCallScreen; + } + + @Override + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case START: + sendEmptyMessage(READ_MESSAGE); + break; + case READ_MESSAGE: + try { + final String message = rttCall.readImmediately(); + if (message != null) { + ThreadUtil.postOnUiThread(() -> rttCallScreen.onRemoteMessage(message)); + } + } catch (IOException e) { + LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "read message", e); + } + sendEmptyMessageDelayed(READ_MESSAGE, 200); + break; + case WRITE_MESSAGE: + try { + rttCall.write((String) msg.obj); + } catch (IOException e) { + LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "write message", e); + } + break; + default: // fall out + } + } + + void start() { + sendEmptyMessage(START); + } + + void writeMessage(String message) { + sendMessage(obtainMessage(WRITE_MESSAGE, message)); + } + } } diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java index 378806183..90a01401c 100644 --- a/java/com/android/incallui/call/DialerCall.java +++ b/java/com/android/incallui/call/DialerCall.java @@ -939,6 +939,14 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa } } + @TargetApi(28) + public RttCall getRttCall() { + if (!isRttCall()) { + return null; + } + return getTelecomCall().getRttCall(); + } + public boolean hasReceivedVideoUpgradeRequest() { return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState()); } @@ -948,7 +956,6 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa } public boolean hasSentRttUpgradeRequest() { - // TODO(wangqi): Implement this. return false; } diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java index 1ea7f31b1..69837188a 100644 --- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java +++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java @@ -110,7 +110,17 @@ public class RttChatAdapter extends RecyclerView.Adapter lastIndexOfLocalMessage) { + lastIndexOfRemoteMessage -= 1; + } + lastIndexOfLocalMessage = -1; + } else { + notifyItemChanged(lastIndexOfLocalMessage); + } } } diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index c7ee2ff67..ba99b2bf6 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -132,7 +132,7 @@ public class RttChatFragment extends Fragment FragmentUtils.getParentUnsafe(this, RttCallScreenDelegateFactory.class) .newRttCallScreenDelegate(this); - rttCallScreenDelegate.initRttCallScreenDelegate(getContext(), this); + rttCallScreenDelegate.initRttCallScreenDelegate(this); inCallScreenDelegate.onInCallScreenDelegateInit(this); inCallScreenDelegate.onInCallScreenReady(); @@ -193,7 +193,24 @@ public class RttChatFragment extends Fragment if (isClearingInput) { return; } - adapter.addLocalMessage(RttChatMessage.getChangedString(s, start, before, count)); + String messageToAppend = RttChatMessage.getChangedString(s, start, before, count); + if (!TextUtils.isEmpty(messageToAppend)) { + adapter.addLocalMessage(messageToAppend); + rttCallScreenDelegate.onLocalMessage(messageToAppend); + } + } + + @Override + public void onRemoteMessage(String message) { + adapter.addRemoteMessage(message); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + LogUtil.enterBlock("RttChatFragment.onDestroyView"); + inCallButtonUiDelegate.onInCallButtonUiUnready(); + inCallScreenDelegate.onInCallScreenUnready(); } @Override diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreen.java b/java/com/android/incallui/rtt/protocol/RttCallScreen.java index afacbae48..916dfb84d 100644 --- a/java/com/android/incallui/rtt/protocol/RttCallScreen.java +++ b/java/com/android/incallui/rtt/protocol/RttCallScreen.java @@ -25,6 +25,8 @@ public interface RttCallScreen { void onRttScreenStop(); + void onRemoteMessage(String message); + Fragment getRttCallScreenFragment(); String getCallId(); diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java index e29c43d70..8c484a844 100644 --- a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java +++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java @@ -16,14 +16,14 @@ package com.android.incallui.rtt.protocol; -import android.content.Context; - /** Callbacks from the module out to the container. */ public interface RttCallScreenDelegate { - void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen); + void initRttCallScreenDelegate(RttCallScreen rttCallScreen); void onRttCallScreenUiReady(); void onRttCallScreenUiUnready(); + + void onLocalMessage(String message); } -- cgit v1.2.3