From d6b10d559f345eab2dd3dd89b07cfc3fd79c59bc Mon Sep 17 00:00:00 2001 From: wangqi Date: Thu, 12 Apr 2018 14:44:06 -0700 Subject: Add RTT transcript to call details. 1. Save RTT transcript when call is destroyed 2. Show RTT transcript link when it's available Bug: 67596257,77717594 Test: manual PiperOrigin-RevId: 192673172 Change-Id: If541ad9137c965166548d2fb449e835b95566727 --- .../dialer/calldetails/CallDetailsActivity.java | 1 - .../calldetails/CallDetailsActivityCommon.java | 50 ++++++++++- .../calldetails/CallDetailsAdapterCommon.java | 3 + .../calldetails/CallDetailsEntryViewHolder.java | 17 ++++ .../calldetails/proto/call_details_entries.proto | 1 + .../calldetails/res/layout/call_details_entry.xml | 37 ++++++--- .../dialer/calldetails/res/values/strings.xml | 6 ++ .../dialer/calldetails/res/values/styles.xml | 12 +++ .../android/dialer/rtt/RttTranscriptContract.java | 40 +++++++++ .../dialer/rtt/RttTranscriptDatabaseHelper.java | 56 +++++++++++++ java/com/android/dialer/rtt/RttTranscriptUtil.java | 97 ++++++++++++++++++++++ .../com/android/dialer/theme/res/values/colors.xml | 2 + java/com/android/incallui/RttCallPresenter.java | 22 +++-- java/com/android/incallui/call/CallList.java | 1 + java/com/android/incallui/call/DialerCall.java | 28 +++++++ .../android/incallui/rtt/impl/RttChatFragment.java | 3 + .../rtt/protocol/RttCallScreenDelegate.java | 2 + 17 files changed, 355 insertions(+), 23 deletions(-) create mode 100644 java/com/android/dialer/rtt/RttTranscriptContract.java create mode 100644 java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java create mode 100644 java/com/android/dialer/rtt/RttTranscriptUtil.java (limited to 'java/com/android') diff --git a/java/com/android/dialer/calldetails/CallDetailsActivity.java b/java/com/android/dialer/calldetails/CallDetailsActivity.java index 4dc294e76..f0f9711ca 100644 --- a/java/com/android/dialer/calldetails/CallDetailsActivity.java +++ b/java/com/android/dialer/calldetails/CallDetailsActivity.java @@ -134,7 +134,6 @@ public final class CallDetailsActivity extends CallDetailsActivityCommon { private void updateCallDetailsEntries(CallDetailsEntries newEntries) { activity.setCallDetailsEntries(newEntries); - activity.getAdapter().updateCallDetailsEntries(newEntries); EnrichedCallComponent.get(activity) .getEnrichedCallManager() .requestAllHistoricalData(activity.getNumber(), newEntries); diff --git a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java index dea1b8cda..46705eb2a 100644 --- a/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java +++ b/java/com/android/dialer/calldetails/CallDetailsActivityCommon.java @@ -26,9 +26,11 @@ import android.os.Bundle; import android.provider.CallLog; import android.provider.CallLog.Calls; import android.support.annotation.CallSuper; +import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresPermission; +import android.support.annotation.WorkerThread; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -45,6 +47,7 @@ import com.android.dialer.common.concurrent.DialerExecutor.FailureListener; import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener; import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.concurrent.UiListener; import com.android.dialer.common.database.Selection; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.duo.Duo; @@ -58,7 +61,9 @@ import com.android.dialer.logging.UiAction; import com.android.dialer.performancereport.PerformanceReport; import com.android.dialer.postcall.PostCall; import com.android.dialer.precall.PreCall; +import com.android.dialer.rtt.RttTranscriptUtil; import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.ListenableFuture; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; @@ -91,6 +96,7 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity { private CallDetailsAdapterCommon adapter; private CallDetailsEntries callDetailsEntries; + private UiListener checkRttTranscriptAvailabilityListener; /** * Handles the intent that launches {@link OldCallDetailsActivity} or {@link CallDetailsActivity}, @@ -121,6 +127,9 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity { }); handleIntent(getIntent()); setupRecyclerViewForEntries(); + checkRttTranscriptAvailabilityListener = + DialerExecutorComponent.get(this) + .createUiListener(getFragmentManager(), "Query RTT transcript availability"); } @Override @@ -143,6 +152,40 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity { EnrichedCallComponent.get(this) .getEnrichedCallManager() .requestAllHistoricalData(getNumber(), callDetailsEntries); + checkRttTranscriptAvailabilityListener.listen( + this, + checkRttTranscriptAvailability(), + this::setCallDetailsEntries, + throwable -> { + throw new RuntimeException(throwable); + }); + } + + private ListenableFuture checkRttTranscriptAvailability() { + return DialerExecutorComponent.get(this) + .backgroundExecutor() + .submit(() -> checkRttTranscriptAvailabilityInBackground(callDetailsEntries)); + } + + /** + * Check RTT transcript availability. + * + * @param input the original {@link CallDetailsEntries} + * @return {@link CallDetailsEntries} with updated RTT transcript availability. + */ + @WorkerThread + private CallDetailsEntries checkRttTranscriptAvailabilityInBackground( + @Nullable CallDetailsEntries input) { + RttTranscriptUtil rttTranscriptUtil = new RttTranscriptUtil(this); + + CallDetailsEntries.Builder mutableCallDetailsEntries = CallDetailsEntries.newBuilder(); + for (CallDetailsEntry entry : input.getEntriesList()) { + CallDetailsEntry.Builder newEntry = CallDetailsEntry.newBuilder().mergeFrom(entry); + newEntry.setHasRttTranscript( + rttTranscriptUtil.checkRttTranscriptAvailability(String.valueOf(entry.getDate()))); + mutableCallDetailsEntries.addEntries(newEntry.build()); + } + return mutableCallDetailsEntries.build(); } @Override @@ -185,8 +228,13 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity { super.onBackPressed(); } + @MainThread protected final void setCallDetailsEntries(CallDetailsEntries entries) { + Assert.isMainThread(); this.callDetailsEntries = entries; + if (adapter != null) { + adapter.updateCallDetailsEntries(entries); + } } protected final CallDetailsEntries getCallDetailsEntries() { @@ -415,7 +463,7 @@ abstract class CallDetailsActivityCommon extends AppCompatActivity { Map> mappedResults = getAllHistoricalData(activity.getNumber(), activity.callDetailsEntries); - activity.adapter.updateCallDetailsEntries( + activity.setCallDetailsEntries( generateAndMapNewCallDetailsEntriesHistoryResults( activity.getNumber(), activity.callDetailsEntries, mappedResults)); } diff --git a/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java index 27feff89d..a79642bad 100644 --- a/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java +++ b/java/com/android/dialer/calldetails/CallDetailsAdapterCommon.java @@ -18,6 +18,7 @@ package com.android.dialer.calldetails; import android.content.Context; import android.support.annotation.CallSuper; +import android.support.annotation.MainThread; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; @@ -137,7 +138,9 @@ abstract class CallDetailsAdapterCommon extends RecyclerView.Adapter + android:maxLines="100"/> + android:layout_marginStart="@dimen/call_entry_text_left_margin" + android:layout_below="@+id/call_type"/> + android:layout_alignParentEnd="true"/> + android:visibility="gone"/> + \ No newline at end of file diff --git a/java/com/android/dialer/calldetails/res/values/strings.xml b/java/com/android/dialer/calldetails/res/values/strings.xml index f81696034..c3fff20bc 100644 --- a/java/com/android/dialer/calldetails/res/values/strings.xml +++ b/java/com/android/dialer/calldetails/res/values/strings.xml @@ -55,4 +55,10 @@ Assisted dialing was used + + + Transcript available only for calls with messaging + + + See transcript diff --git a/java/com/android/dialer/calldetails/res/values/styles.xml b/java/com/android/dialer/calldetails/res/values/styles.xml index 1a2b52994..93567ef73 100644 --- a/java/com/android/dialer/calldetails/res/values/styles.xml +++ b/java/com/android/dialer/calldetails/res/values/styles.xml @@ -27,4 +27,16 @@ #8A000000 14sp + + + \ No newline at end of file diff --git a/java/com/android/dialer/rtt/RttTranscriptContract.java b/java/com/android/dialer/rtt/RttTranscriptContract.java new file mode 100644 index 000000000..ddc9c5345 --- /dev/null +++ b/java/com/android/dialer/rtt/RttTranscriptContract.java @@ -0,0 +1,40 @@ +/* + * 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.dialer.rtt; + +/** Contract for the RTT transcript database. */ +public final class RttTranscriptContract { + + /** Columns for RTT transcript. */ + static final class RttTranscriptColumn { + + /** + * Unique key that should match {@link android.provider.CallLog.Calls#DATE} of the data row it + * is associated with. + * + *

TYPE: TEXT + */ + static final String TRANSCRIPT_ID = "rtt_transcript_id"; + + /** + * Transcript data, encoded as {@link RttTranscript} proto. + * + *

TYPE: BLOB + */ + static final String TRANSCRIPT_DATA = "transcript_data"; + } +} diff --git a/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java b/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java new file mode 100644 index 000000000..934eedb5d --- /dev/null +++ b/java/com/android/dialer/rtt/RttTranscriptDatabaseHelper.java @@ -0,0 +1,56 @@ +/* + * 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.dialer.rtt; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.os.SystemClock; +import com.android.dialer.common.LogUtil; +import com.android.dialer.rtt.RttTranscriptContract.RttTranscriptColumn; + +/** Database helper class for RTT transcript. */ +final class RttTranscriptDatabaseHelper extends SQLiteOpenHelper { + + static final String TABLE = "rtt_transcript"; + + private static final String CREATE_TABLE_SQL = + "create table if not exists " + + TABLE + + " (" + + (RttTranscriptColumn.TRANSCRIPT_ID + " integer primary key, ") + + (RttTranscriptColumn.TRANSCRIPT_DATA + " blob not null") + + ");"; + + RttTranscriptDatabaseHelper(Context context) { + super(context, "rtt_transcript.db", null, 1); + } + + @Override + public void onCreate(SQLiteDatabase db) { + LogUtil.enterBlock("RttTranscriptDatabaseHelper.onCreate"); + long startTime = SystemClock.elapsedRealtime(); + db.execSQL(CREATE_TABLE_SQL); + LogUtil.i( + "RttTranscriptDatabaseHelper.onCreate", + "took: %dms", + SystemClock.elapsedRealtime() - startTime); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} +} diff --git a/java/com/android/dialer/rtt/RttTranscriptUtil.java b/java/com/android/dialer/rtt/RttTranscriptUtil.java new file mode 100644 index 000000000..e55d2ea55 --- /dev/null +++ b/java/com/android/dialer/rtt/RttTranscriptUtil.java @@ -0,0 +1,97 @@ +/* + * 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.dialer.rtt; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.support.annotation.WorkerThread; +import com.android.dialer.common.Assert; +import com.android.dialer.rtt.RttTranscriptContract.RttTranscriptColumn; +import com.google.protobuf.InvalidProtocolBufferException; + +/** Util class to save and load RTT transcript. */ +public final class RttTranscriptUtil { + + private final RttTranscriptDatabaseHelper databaseHelper; + + public RttTranscriptUtil(Context context) { + databaseHelper = new RttTranscriptDatabaseHelper(context); + } + + /** @return true if there is RTT transcript available. */ + @WorkerThread + public boolean checkRttTranscriptAvailability(String transcriptId) { + Assert.isWorkerThread(); + try (Cursor cursor = + databaseHelper + .getReadableDatabase() + .query( + RttTranscriptDatabaseHelper.TABLE, + new String[] {RttTranscriptColumn.TRANSCRIPT_ID}, + RttTranscriptColumn.TRANSCRIPT_ID + " = ?", + new String[] {transcriptId}, + null, + null, + null)) { + if (cursor != null && cursor.moveToFirst()) { + return true; + } else { + return false; + } + } + } + + @WorkerThread + public RttTranscript getRttTranscript(String transcriptId) { + Assert.isWorkerThread(); + try (Cursor cursor = + databaseHelper + .getReadableDatabase() + .query( + RttTranscriptDatabaseHelper.TABLE, + new String[] {RttTranscriptColumn.TRANSCRIPT_DATA}, + RttTranscriptColumn.TRANSCRIPT_ID + " = ?", + new String[] {transcriptId}, + null, + null, + null)) { + if (cursor != null && cursor.moveToFirst()) { + try { + return RttTranscript.parseFrom(cursor.getBlob(0)); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException("Parse failed for RTT transcript", e); + } + } else { + return null; + } + } + } + + @WorkerThread + public void saveRttTranscript(RttTranscript rttTranscript) { + Assert.isWorkerThread(); + ContentValues value = new ContentValues(); + value.put(RttTranscriptColumn.TRANSCRIPT_ID, rttTranscript.getId()); + value.put(RttTranscriptColumn.TRANSCRIPT_DATA, rttTranscript.toByteArray()); + long id = + databaseHelper.getWritableDatabase().insert(RttTranscriptDatabaseHelper.TABLE, null, value); + if (id < 0) { + throw new RuntimeException("Failed to save RTT transcript"); + } + } +} diff --git a/java/com/android/dialer/theme/res/values/colors.xml b/java/com/android/dialer/theme/res/values/colors.xml index 6b98e208b..e80fc4b30 100644 --- a/java/com/android/dialer/theme/res/values/colors.xml +++ b/java/com/android/dialer/theme/res/values/colors.xml @@ -46,6 +46,8 @@ #61000000 + #2A56C6 + @color/dialer_theme_color diff --git a/java/com/android/incallui/RttCallPresenter.java b/java/com/android/incallui/RttCallPresenter.java index 5e8390793..21e28cee4 100644 --- a/java/com/android/incallui/RttCallPresenter.java +++ b/java/com/android/incallui/RttCallPresenter.java @@ -63,28 +63,34 @@ public class RttCallPresenter implements RttCallScreenDelegate, InCallStateListe LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiReady"); InCallPresenter.getInstance().addListener(this); startListenOnRemoteMessage(); - DialerCall call = CallList.getInstance().getActiveCall(); + DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId()); if (call != null) { rttCallScreen.onRestoreRttChat(call.getRttTranscript()); } } + @Override + public void onSaveRttTranscript() { + LogUtil.enterBlock("RttCallPresenter.onSaveRttTranscript"); + DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId()); + if (call != null) { + saveTranscript(call); + } + } + @Override public void onRttCallScreenUiUnready() { LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiUnready"); InCallPresenter.getInstance().removeListener(this); stopListenOnRemoteMessage(); - DialerCall call = CallList.getInstance().getActiveCall(); - if (call != null) { - saveTranscript(call); - } + onSaveRttTranscript(); } private void saveTranscript(DialerCall dialerCall) { LogUtil.enterBlock("RttCallPresenter.saveTranscript"); RttTranscript.Builder builder = RttTranscript.newBuilder(); builder - .setId(dialerCall.getNumber() + dialerCall.getCreationTimeMillis()) + .setId(String.valueOf(dialerCall.getCreationTimeMillis())) .setTimestamp(dialerCall.getCreationTimeMillis()) .setNumber(dialerCall.getNumber()) .addAllMessages(rttCallScreen.getRttTranscriptMessageList()); @@ -100,9 +106,9 @@ public class RttCallPresenter implements RttCallScreenDelegate, InCallStateListe } private void startListenOnRemoteMessage() { - DialerCall call = CallList.getInstance().getActiveCall(); + DialerCall call = CallList.getInstance().getCallById(rttCallScreen.getCallId()); if (call == null) { - LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call is not active yet"); + LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call does not exist"); return; } rttCall = call.getRttCall(); diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java index eccdceeca..04e02c45b 100644 --- a/java/com/android/incallui/call/CallList.java +++ b/java/com/android/incallui/call/CallList.java @@ -745,6 +745,7 @@ public class CallList implements DialerCallDelegate { call.setState(DialerCall.State.IDLE); updateCallInMap(call); notifyGenericListeners(); + call.onDestroy(); } /** diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java index 3372c0329..5d32f46ad 100644 --- a/java/com/android/incallui/call/DialerCall.java +++ b/java/com/android/incallui/call/DialerCall.java @@ -57,6 +57,8 @@ import com.android.dialer.callintent.CallIntentParser; import com.android.dialer.callintent.CallSpecificAppData; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DefaultFutureCallback; +import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.compat.telephony.TelephonyManagerCompat; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.duo.DuoComponent; @@ -74,6 +76,7 @@ import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.preferredsim.PreferredAccountRecorder; import com.android.dialer.rtt.RttTranscript; +import com.android.dialer.rtt.RttTranscriptUtil; import com.android.dialer.telecom.TelecomCallUtil; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.theme.R; @@ -86,6 +89,9 @@ import com.android.incallui.videotech.duo.DuoVideoTech; import com.android.incallui.videotech.empty.EmptyVideoTech; import com.android.incallui.videotech.ims.ImsVideoTech; import com.android.incallui.videotech.utils.VideoUtils; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -536,6 +542,28 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa return countryIso; } + /** + * Called when call is disconnected and removed from {@link CallList}, UI may already be destroyed + * at this point. This is last chance to do something for the call. + */ + public void onDestroy() { + LogUtil.enterBlock("DialerCall.onDestroy"); + if (rttTranscript != null) { + RttTranscript rttTranscriptToSave = rttTranscript; + ListenableFuture future = + DialerExecutorComponent.get(context) + .backgroundExecutor() + .submit( + () -> { + new RttTranscriptUtil(context).saveRttTranscript(rttTranscriptToSave); + return null; + }); + Futures.addCallback(future, new DefaultFutureCallback<>(), MoreExecutors.directExecutor()); + // Sets to null so it won't be saved again when called multiple times. + rttTranscript = null; + } + } + private void updateIsVoiceMailNumber() { if (getHandle() != null && PhoneAccount.SCHEME_VOICEMAIL.equals(getHandle().getScheme())) { isVoicemailNumber = true; diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index 15baa8cfa..2d70b6b12 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -462,6 +462,9 @@ public class RttChatFragment extends Fragment } else { hideWaitingForJoinBanner(); } + if (primaryCallState.state() == State.DISCONNECTED) { + rttCallScreenDelegate.onSaveRttTranscript(); + } } private void showWaitingForJoinBanner() { diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java index 8c484a844..990c0759e 100644 --- a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java +++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java @@ -26,4 +26,6 @@ public interface RttCallScreenDelegate { void onRttCallScreenUiUnready(); void onLocalMessage(String message); + + void onSaveRttTranscript(); } -- cgit v1.2.3