diff options
Diffstat (limited to 'java/com/android')
57 files changed, 1348 insertions, 368 deletions
diff --git a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java index 0f00a5d82..35f854010 100644 --- a/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java +++ b/java/com/android/dialer/binary/aosp/AospDialerRootComponent.java @@ -26,6 +26,7 @@ import com.android.dialer.enrichedcall.stub.StubEnrichedCallModule; import com.android.dialer.feedback.stub.StubFeedbackModule; import com.android.dialer.glidephotomanager.GlidePhotoManagerModule; import com.android.dialer.inject.ContextModule; +import com.android.dialer.metrics.StubMetricsModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; import com.android.dialer.precall.impl.PreCallModule; @@ -63,6 +64,7 @@ import javax.inject.Singleton; StubDuoModule.class, StubEnrichedCallModule.class, StubNewBubbleModule.class, + StubMetricsModule.class, StubFeedbackModule.class, StubMapsModule.class, StubSimSuggestionModule.class, diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java index 3e7db9d8a..cd95c3ee7 100644 --- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java +++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java @@ -27,6 +27,7 @@ import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.feedback.FeedbackComponent; import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent; import com.android.dialer.main.MainComponent; +import com.android.dialer.metrics.MetricsComponent; import com.android.dialer.phonelookup.PhoneLookupComponent; import com.android.dialer.phonenumbergeoutil.PhoneNumberGeoUtilComponent; import com.android.dialer.precall.PreCallComponent; @@ -59,6 +60,7 @@ public interface BaseDialerRootComponent GlidePhotoManagerComponent.HasComponent, MainComponent.HasComponent, MapsComponent.HasComponent, + MetricsComponent.HasComponent, NewBubbleComponent.HasComponent, PhoneLookupComponent.HasComponent, PhoneNumberGeoUtilComponent.HasComponent, diff --git a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java index d4520f33e..497d97724 100644 --- a/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java +++ b/java/com/android/dialer/binary/google/GoogleStubDialerRootComponent.java @@ -26,6 +26,7 @@ import com.android.dialer.enrichedcall.stub.StubEnrichedCallModule; import com.android.dialer.feedback.stub.StubFeedbackModule; import com.android.dialer.glidephotomanager.GlidePhotoManagerModule; import com.android.dialer.inject.ContextModule; +import com.android.dialer.metrics.StubMetricsModule; import com.android.dialer.phonelookup.PhoneLookupModule; import com.android.dialer.phonenumbergeoutil.impl.PhoneNumberGeoUtilModule; import com.android.dialer.precall.impl.PreCallModule; @@ -67,6 +68,7 @@ import javax.inject.Singleton; StubDuoModule.class, StubEnrichedCallModule.class, StubFeedbackModule.class, + StubMetricsModule.class, StubNewBubbleModule.class, StubSimSuggestionModule.class, StubSpamModule.class, diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java index 52570c05e..8dec43759 100644 --- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java +++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java @@ -28,18 +28,17 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import com.android.dialer.DialerPhoneNumber; -import com.android.dialer.NumberAttributes; import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog; import com.android.dialer.calllog.datasources.CallLogDataSource; import com.android.dialer.calllog.datasources.CallLogMutations; import com.android.dialer.calllog.datasources.util.RowCombiner; +import com.android.dialer.calllogutils.NumberAttributesConverter; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.common.concurrent.Annotations.LightweightExecutor; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; -import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.google.common.collect.ImmutableMap; @@ -573,23 +572,8 @@ public final class PhoneLookupDataSource } private void updateContentValues(ContentValues contentValues, PhoneLookupInfo phoneLookupInfo) { - PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = - new PhoneLookupInfoConsolidator(phoneLookupInfo); contentValues.put( AnnotatedCallLog.NUMBER_ATTRIBUTES, - NumberAttributes.newBuilder() - .setName(phoneLookupInfoConsolidator.getName()) - .setPhotoUri(phoneLookupInfoConsolidator.getPhotoUri()) - .setPhotoId(phoneLookupInfoConsolidator.getPhotoId()) - .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) - .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) - .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) - .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) - .setIsBlocked(phoneLookupInfoConsolidator.isBlocked()) - .setIsSpam(phoneLookupInfoConsolidator.isSpam()) - .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) - .setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isCp2LocalInfoIncomplete()) - .build() - .toByteArray()); + NumberAttributesConverter.fromPhoneLookupInfo(phoneLookupInfo).build().toByteArray()); } } diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java index 5083a95c5..69c431953 100644 --- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java +++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java @@ -23,8 +23,8 @@ import android.support.annotation.MainThread; import android.support.annotation.VisibleForTesting; import android.util.ArrayMap; import com.android.dialer.DialerPhoneNumber; -import com.android.dialer.NumberAttributes; import com.android.dialer.calllog.model.CoalescedRow; +import com.android.dialer.calllogutils.NumberAttributesConverter; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; @@ -33,7 +33,6 @@ import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.inject.ApplicationContext; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; -import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; import com.google.common.collect.ImmutableMap; @@ -198,23 +197,8 @@ public final class RealtimeRowProcessor { private CoalescedRow applyPhoneLookupInfoToRow( PhoneLookupInfo phoneLookupInfo, CoalescedRow row) { - PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = - new PhoneLookupInfoConsolidator(phoneLookupInfo); return row.toBuilder() - .setNumberAttributes( - // TODO(zachh): Put this in a common location. - NumberAttributes.newBuilder() - .setName(phoneLookupInfoConsolidator.getName()) - .setPhotoUri(phoneLookupInfoConsolidator.getPhotoUri()) - .setPhotoId(phoneLookupInfoConsolidator.getPhotoId()) - .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) - .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) - .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) - .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) - .setIsBlocked(phoneLookupInfoConsolidator.isBlocked()) - .setIsSpam(phoneLookupInfoConsolidator.isSpam()) - .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) - .build()) + .setNumberAttributes(NumberAttributesConverter.fromPhoneLookupInfo(phoneLookupInfo).build()) .build(); } } diff --git a/java/com/android/dialer/calllogutils/CallLogEntryText.java b/java/com/android/dialer/calllogutils/CallLogEntryText.java index 737b1d30f..ab851cbbd 100644 --- a/java/com/android/dialer/calllogutils/CallLogEntryText.java +++ b/java/com/android/dialer/calllogutils/CallLogEntryText.java @@ -70,7 +70,8 @@ public final class CallLogEntryText { * <ul> * <li>For numbers that are not spam or blocked: (Duo video, )?$Label|$Location • Date * <li>For blocked non-spam numbers: Blocked • (Duo video, )?$Label|$Location • Date - * <li>For spam numbers: Spam • (Duo video, )?$Label • Date + * <li>For spam but not blocked numbers: Spam • (Duo video, )?$Label • Date + * <li>For blocked spam numbers: Blocked • Spam • (Duo video, )?$Label • Date * </ul> * * <p>Examples: @@ -84,6 +85,7 @@ public final class CallLogEntryText { * <li>Blocked • Brooklyn, NJ • 10 min ago * <li>Spam • Mobile • Now * <li>Spam • Now + * <li>Blocked • Spam • Mobile • Now * <li>Brooklyn, NJ • Jan 15 * </ul> * @@ -93,11 +95,11 @@ public final class CallLogEntryText { Context context, Clock clock, CoalescedRow row) { List<CharSequence> components = new ArrayList<>(); - // If a number is both spam and blocked, only show "Spam". + if (row.numberAttributes().getIsBlocked()) { + components.add(context.getText(R.string.new_call_log_secondary_blocked)); + } if (row.numberAttributes().getIsSpam()) { components.add(context.getText(R.string.new_call_log_secondary_spam)); - } else if (row.numberAttributes().getIsBlocked()) { - components.add(context.getText(R.string.new_call_log_secondary_blocked)); } components.add(getNumberTypeLabel(context, row)); @@ -121,8 +123,10 @@ public final class CallLogEntryText { * (Duo video, )?$Label|$Location [• NumberIfNoName]? * For blocked non-spam numbers: * Blocked • (Duo video, )?$Label|$Location [• NumberIfNoName]? - * For spam numbers: + * For spam but not blocked numbers: * Spam • (Duo video, )?$Label [• NumberIfNoName]? + * For blocked spam numbers: + * Blocked • Spam • (Duo video, )?$Label [• NumberIfNoName]? * * The number is shown at the end if there is no name for the entry. (It is shown in primary * text otherwise.) @@ -139,11 +143,11 @@ public final class CallLogEntryText { */ List<CharSequence> components = new ArrayList<>(); - // If a number is both spam and blocked, only show "Spam". + if (row.numberAttributes().getIsBlocked()) { + components.add(context.getText(R.string.new_call_log_secondary_blocked)); + } if (row.numberAttributes().getIsSpam()) { components.add(context.getText(R.string.new_call_log_secondary_spam)); - } else if (row.numberAttributes().getIsBlocked()) { - components.add(context.getText(R.string.new_call_log_secondary_blocked)); } components.add(getNumberTypeLabel(context, row)); diff --git a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java index bed1edd06..efd1d72c2 100644 --- a/java/com/android/dialer/calllogutils/NumberAttributesConverter.java +++ b/java/com/android/dialer/calllogutils/NumberAttributesConverter.java @@ -16,13 +16,16 @@ package com.android.dialer.calllogutils; +import android.text.TextUtils; import com.android.dialer.NumberAttributes; import com.android.dialer.glidephotomanager.PhotoInfo; +import com.android.dialer.phonelookup.PhoneLookupInfo; +import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; /** Converts {@link NumberAttributes} to {@link PhotoInfo} */ public final class NumberAttributesConverter { - /** Converts to {@link PhotoInfo.Builder} */ + /** Converts {@link NumberAttributes} to {@link PhotoInfo.Builder} */ public static PhotoInfo.Builder toPhotoInfoBuilder(NumberAttributes numberAttributes) { return PhotoInfo.builder() .setName(numberAttributes.getName()) @@ -34,4 +37,25 @@ public final class NumberAttributesConverter { .setIsVoicemail(numberAttributes.getIsVoicemail()) .setIsBlocked(numberAttributes.getIsBlocked()); } + + /** Converts {@link PhoneLookupInfo} to {@link NumberAttributes.Builder} */ + public static NumberAttributes.Builder fromPhoneLookupInfo(PhoneLookupInfo phoneLookupInfo) { + PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = + new PhoneLookupInfoConsolidator(phoneLookupInfo); + return NumberAttributes.newBuilder() + .setName(phoneLookupInfoConsolidator.getName()) + .setPhotoUri( + !TextUtils.isEmpty(phoneLookupInfoConsolidator.getPhotoThumbnailUri()) + ? phoneLookupInfoConsolidator.getPhotoThumbnailUri() + : phoneLookupInfoConsolidator.getPhotoUri()) + .setPhotoId(phoneLookupInfoConsolidator.getPhotoId()) + .setLookupUri(phoneLookupInfoConsolidator.getLookupUri()) + .setNumberTypeLabel(phoneLookupInfoConsolidator.getNumberLabel()) + .setIsBusiness(phoneLookupInfoConsolidator.isBusiness()) + .setIsVoicemail(phoneLookupInfoConsolidator.isVoicemail()) + .setIsBlocked(phoneLookupInfoConsolidator.isBlocked()) + .setIsSpam(phoneLookupInfoConsolidator.isSpam()) + .setCanReportAsInvalidNumber(phoneLookupInfoConsolidator.canReportAsInvalidNumber()) + .setIsCp2InfoIncomplete(phoneLookupInfoConsolidator.isCp2LocalInfoIncomplete()); + } } diff --git a/java/com/android/dialer/common/res/values/strings.xml b/java/com/android/dialer/common/res/values/strings.xml index 770f42f37..53a2b56d7 100644 --- a/java/com/android/dialer/common/res/values/strings.xml +++ b/java/com/android/dialer/common/res/values/strings.xml @@ -17,4 +17,6 @@ <resources> <string name="network_name_wifi">Wifi</string> <string name="network_name_mobile">Mobile</string> + <!-- Content description for the overflow menu button. [CHAR LIMIT=NONE] --> + <string name="content_description_overflow">More options</string> </resources> diff --git a/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java b/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java index c6d92057d..10c4dfb4c 100644 --- a/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java +++ b/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java @@ -68,13 +68,13 @@ public class GlidePhotoManagerImpl implements GlidePhotoManager { // Warning: Glide ignores extra attributes on BitmapDrawable such as tint and draw the bitmap // directly so be sure not to set tint in the XML of any drawable referenced below. - // The spam status takes precedence over whether the number is blocked. - if (photoInfo.isSpam()) { - return requestManager.load(R.drawable.ic_report_red_48dp); - } + // Whether the number is blocked takes precedence over the spam status. if (photoInfo.isBlocked()) { return requestManager.load(R.drawable.ic_block_grey_48dp); } + if (photoInfo.isSpam()) { + return requestManager.load(R.drawable.ic_report_red_48dp); + } if (!TextUtils.isEmpty(photoInfo.photoUri())) { return requestManager.load(parseUri(photoInfo.photoUri())); } diff --git a/java/com/android/dialer/logging/dialer_impression.proto b/java/com/android/dialer/logging/dialer_impression.proto index f839b1399..635d8fd02 100644 --- a/java/com/android/dialer/logging/dialer_impression.proto +++ b/java/com/android/dialer/logging/dialer_impression.proto @@ -12,7 +12,7 @@ message DialerImpression { // Event enums to be used for Impression Logging in Dialer. // It's perfectly acceptable for this enum to be large // Values should be from 1000 to 100000. - // Next Tag: 1327 + // Next Tag: 1341 enum Type { UNKNOWN_AOSP_EVENT_TYPE = 1000; @@ -657,5 +657,23 @@ message DialerImpression { DUO_CALL_LOG_SET_UP_INSTALL_SHOWN = 1324; DUO_CALL_LOG_SET_UP_ACTIVATE_SHOWN = 1325; DUO_CALL_LOG_INVITE_SHOWN = 1326; + + // NUI bottom navigation bar + NUI_SWITCH_TAB_TO_FAVORITE = 1327; + NUI_SWITCH_TAB_TO_CALL_LOG = 1328; + NUI_SWITCH_TAB_TO_CONTACTS = 1329; + NUI_SWITCH_TAB_TO_VOICEMAIL = 1330; + // NUI search + NUI_TOUCH_DIALPAD_SEARCH_LIST_TO_CLOSE_SEARCH_AND_DIALPAD = 1331; + NUI_TOUCH_DIALPAD_SEARCH_LIST_TO_HIDE_DIALPAD = 1332; + NUI_TOUCH_SEARCH_LIST_TO_CLOSE_SEARCH = 1333; + NUI_TOUCH_SEARCH_LIST_TO_HIDE_KEYBOARD = 1334; + NUI_PRESS_BACK_BUTTON_TO_CLOSE_SEARCH = 1335; + NUI_PRESS_BACK_BUTTON_TO_CLOSE_SEARCH_AND_DIALPAD = 1336; + NUI_PRESS_BACK_BUTTON_TO_HIDE_DIALPAD = 1337; + NUI_CLICK_SEARCH_BAR = 1338; + NUI_CLICK_SEARCH_BAR_VOICE_BUTTON = 1339; + // NUI FAB + NUI_CLICK_FAB_TO_OPEN_DIALPAD = 1340; } } diff --git a/java/com/android/dialer/main/MainActivityPeer.java b/java/com/android/dialer/main/MainActivityPeer.java index 6457b607b..c1a328a65 100644 --- a/java/com/android/dialer/main/MainActivityPeer.java +++ b/java/com/android/dialer/main/MainActivityPeer.java @@ -28,6 +28,8 @@ public interface MainActivityPeer { void onActivityStop(); + void onActivityDestroyed(); + void onNewIntent(Intent intent); void onActivityResult(int requestCode, int resultCode, Intent data); diff --git a/java/com/android/dialer/main/impl/MainSearchController.java b/java/com/android/dialer/main/impl/MainSearchController.java index 7098f8844..9b734f40c 100644 --- a/java/com/android/dialer/main/impl/MainSearchController.java +++ b/java/com/android/dialer/main/impl/MainSearchController.java @@ -40,6 +40,7 @@ import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.dialpadview.DialpadFragment; import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; import com.android.dialer.dialpadview.DialpadFragment.OnDialpadQueryChangedListener; +import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ScreenEvent; import com.android.dialer.main.impl.bottomnav.BottomNavBar; @@ -204,14 +205,23 @@ public class MainSearchController implements SearchBarListener { public void onSearchListTouch() { if (isDialpadVisible()) { if (TextUtils.isEmpty(getDialpadFragment().getQuery())) { + Logger.get(mainActivity) + .logImpression( + DialerImpression.Type.NUI_TOUCH_DIALPAD_SEARCH_LIST_TO_CLOSE_SEARCH_AND_DIALPAD); closeSearch(true); } else { + Logger.get(mainActivity) + .logImpression(DialerImpression.Type.NUI_TOUCH_DIALPAD_SEARCH_LIST_TO_HIDE_DIALPAD); hideDialpad(/* animate=*/ true, /* bottomNavVisible=*/ false); } } else if (isSearchVisible()) { if (TextUtils.isEmpty(toolbar.getQuery())) { + Logger.get(mainActivity) + .logImpression(DialerImpression.Type.NUI_TOUCH_SEARCH_LIST_TO_CLOSE_SEARCH); closeSearch(true); } else { + Logger.get(mainActivity) + .logImpression(DialerImpression.Type.NUI_TOUCH_SEARCH_LIST_TO_HIDE_KEYBOARD); toolbar.hideKeyboard(); } } @@ -225,10 +235,17 @@ public class MainSearchController implements SearchBarListener { public boolean onBackPressed() { if (isDialpadVisible() && !TextUtils.isEmpty(getDialpadFragment().getQuery())) { LogUtil.i("MainSearchController#onBackPressed", "Dialpad visible with query"); + Logger.get(mainActivity) + .logImpression(DialerImpression.Type.NUI_PRESS_BACK_BUTTON_TO_HIDE_DIALPAD); hideDialpad(/* animate=*/ true, /* bottomNavVisible=*/ false); return true; } else if (isSearchVisible()) { LogUtil.i("MainSearchController#onBackPressed", "Search is visible"); + Logger.get(mainActivity) + .logImpression( + isDialpadVisible() + ? DialerImpression.Type.NUI_PRESS_BACK_BUTTON_TO_CLOSE_SEARCH_AND_DIALPAD + : DialerImpression.Type.NUI_PRESS_BACK_BUTTON_TO_CLOSE_SEARCH); closeSearch(true); return true; } else { @@ -252,16 +269,13 @@ public class MainSearchController implements SearchBarListener { mainActivity.getFragmentManager().beginTransaction().remove(getSearchFragment()).commit(); // Clear the dialpad so the phone number isn't persisted between search sessions. - getDialpadFragment().clearDialpad(); + if (getDialpadFragment() != null) { + getDialpadFragment().clearDialpad(); + } } - /** - * Returns {@link DialpadFragment}. - * - * <p>Unless this method is being called for the first time in {@link #openSearch(Optional)} or - * {@link #showDialpad(boolean)}, it should never return null. - */ - private DialpadFragment getDialpadFragment() { + @Nullable + protected DialpadFragment getDialpadFragment() { return (DialpadFragment) mainActivity.getFragmentManager().findFragmentByTag(DIALPAD_FRAGMENT_TAG); } @@ -297,6 +311,7 @@ public class MainSearchController implements SearchBarListener { */ @Override public void onSearchBarClicked() { + Logger.get(mainActivity).logImpression(DialerImpression.Type.NUI_CLICK_SEARCH_BAR); openSearch(Optional.absent()); } @@ -316,14 +331,6 @@ public class MainSearchController implements SearchBarListener { transaction.show(getSearchFragment()); } - // Add the dialpad fragment but keep it hidden - if (getDialpadFragment() == null) { - DialpadFragment dialpadFragment = new DialpadFragment(); - transaction - .add(R.id.dialpad_fragment_container, dialpadFragment, DIALPAD_FRAGMENT_TAG) - .hide(dialpadFragment); - } - transaction.commit(); } @@ -350,6 +357,7 @@ public class MainSearchController implements SearchBarListener { @Override public void onVoiceButtonClicked(VoiceSearchResultCallback voiceSearchResultCallback) { + Logger.get(mainActivity).logImpression(DialerImpression.Type.NUI_CLICK_SEARCH_BAR_VOICE_BUTTON); try { Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mainActivity.startActivityForResult(voiceIntent, ActivityRequestCodes.DIALTACTS_VOICE_SEARCH); diff --git a/java/com/android/dialer/main/impl/NewMainActivityPeer.java b/java/com/android/dialer/main/impl/NewMainActivityPeer.java index 0a85667a1..ed67df936 100644 --- a/java/com/android/dialer/main/impl/NewMainActivityPeer.java +++ b/java/com/android/dialer/main/impl/NewMainActivityPeer.java @@ -60,6 +60,9 @@ public class NewMainActivityPeer implements MainActivityPeer { public void onActivityStop() {} @Override + public void onActivityDestroyed() {} + + @Override public void onNewIntent(Intent intent) {} @Override diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java index c46e61bd0..07c7185ae 100644 --- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java +++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java @@ -66,6 +66,8 @@ import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; import com.android.dialer.dialpadview.DialpadFragment.LastOutgoingCallCallback; import com.android.dialer.dialpadview.DialpadFragment.OnDialpadQueryChangedListener; import com.android.dialer.interactions.PhoneNumberInteraction; +import com.android.dialer.logging.DialerImpression; +import com.android.dialer.logging.Logger; import com.android.dialer.main.MainActivityPeer; import com.android.dialer.main.impl.bottomnav.BottomNavBar; import com.android.dialer.main.impl.bottomnav.BottomNavBar.OnBottomNavTabSelectedListener; @@ -154,6 +156,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onActivityCreate(Bundle savedInstanceState) { + LogUtil.enterBlock("OldMainActivityPeer.onActivityCreate"); mainActivity.setContentView(R.layout.main_activity); initUiListeners(); initLayout(savedInstanceState); @@ -173,14 +176,19 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen snackbarContainer = mainActivity.findViewById(R.id.coordinator_layout); FloatingActionButton fab = mainActivity.findViewById(R.id.fab); - fab.setOnClickListener(v -> searchController.showDialpad(true)); + fab.setOnClickListener( + v -> { + Logger.get(mainActivity) + .logImpression(DialerImpression.Type.NUI_CLICK_FAB_TO_OPEN_DIALPAD); + searchController.showDialpad(true); + }); MainToolbar toolbar = mainActivity.findViewById(R.id.toolbar); mainActivity.setSupportActionBar(mainActivity.findViewById(R.id.toolbar)); bottomNav = mainActivity.findViewById(R.id.bottom_nav_bar); MainBottomNavBarBottomNavTabListener bottomNavTabListener = - new MainBottomNavBarBottomNavTabListener(mainActivity.getFragmentManager()); + new MainBottomNavBarBottomNavTabListener(mainActivity, mainActivity.getFragmentManager()); bottomNav.addOnTabSelectedListener(bottomNavTabListener); callLogFragmentListener = @@ -191,7 +199,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen searchController = getNewMainSearchController(bottomNav, fab, toolbar); toolbar.setSearchBarListener(searchController); - onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); + onDialpadQueryChangedListener = getNewOnDialpadQueryChangedListener(searchController); dialpadListener = new MainDialpadListener(mainActivity, searchController, getLastOutgoingCallListener); searchFragmentListener = new MainSearchFragmentListener(searchController); @@ -222,6 +230,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onNewIntent(Intent intent) { + LogUtil.enterBlock("OldMainActivityPeer.onNewIntent"); showTabOnIntent(intent); } @@ -285,6 +294,9 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen mainActivity.getSystemService(KeyguardManager.class).isKeyguardLocked()); } + @Override + public void onActivityDestroyed() {} + private void showPostCallPrompt() { if (TelecomUtil.isInManagedCall(mainActivity)) { // No prompt to show if the user is in a call @@ -309,10 +321,15 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + LogUtil.i( + "OldMainActivityPeer.onActivityResult", + "requestCode:%d, resultCode:%d", + requestCode, + resultCode); if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) { searchController.onVoiceResults(resultCode, data); } else { - LogUtil.e("MainActivity.onActivityResult", "Unknown request code: " + requestCode); + LogUtil.e("OldMainActivityPeer.onActivityResult", "Unknown request code: " + requestCode); } } @@ -350,6 +367,8 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen return (T) onPhoneNumberPickerActionListener; } else if (callbackInterface.isInstance(oldSpeedDialFragmentHost)) { return (T) oldSpeedDialFragmentHost; + } else if (callbackInterface.isInstance(searchController)) { + return (T) searchController; } else { return null; } @@ -360,6 +379,11 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen return new MainSearchController(mainActivity, bottomNavBar, fab, mainToolbar); } + public MainOnDialpadQueryChangedListener getNewOnDialpadQueryChangedListener( + MainSearchController mainSearchController) { + return new MainOnDialpadQueryChangedListener(mainSearchController); + } + /** @see OnContactSelectedListener */ private static final class MainOnContactSelectedListener implements OnContactSelectedListener { @@ -378,12 +402,12 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen } /** @see OnDialpadQueryChangedListener */ - private static final class MainOnDialpadQueryChangedListener + protected static class MainOnDialpadQueryChangedListener implements OnDialpadQueryChangedListener { private final MainSearchController searchController; - MainOnDialpadQueryChangedListener(MainSearchController searchController) { + protected MainOnDialpadQueryChangedListener(MainSearchController searchController) { this.searchController = searchController; } @@ -794,13 +818,20 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen private static final String VOICEMAIL_TAG = "voicemail"; private final FragmentManager fragmentManager; + private final Context context; + @TabIndex private int selectedTab = -1; - private MainBottomNavBarBottomNavTabListener(FragmentManager fragmentManager) { + private MainBottomNavBarBottomNavTabListener(Context context, FragmentManager fragmentManager) { this.fragmentManager = fragmentManager; + this.context = context; } @Override public void onSpeedDialSelected() { + if (selectedTab != TabIndex.SPEED_DIAL) { + Logger.get(context).logImpression(DialerImpression.Type.NUI_SWITCH_TAB_TO_FAVORITE); + selectedTab = TabIndex.SPEED_DIAL; + } hideAllFragments(); Fragment fragment = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG); if (fragment == null) { @@ -815,6 +846,10 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onCallLogSelected() { + if (selectedTab != TabIndex.CALL_LOG) { + Logger.get(context).logImpression(DialerImpression.Type.NUI_SWITCH_TAB_TO_CALL_LOG); + selectedTab = TabIndex.CALL_LOG; + } hideAllFragments(); CallLogFragment fragment = (CallLogFragment) fragmentManager.findFragmentByTag(CALL_LOG_TAG); if (fragment == null) { @@ -829,6 +864,10 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onContactsSelected() { + if (selectedTab != TabIndex.CONTACTS) { + Logger.get(context).logImpression(DialerImpression.Type.NUI_SWITCH_TAB_TO_CONTACTS); + selectedTab = TabIndex.CONTACTS; + } hideAllFragments(); ContactsFragment fragment = (ContactsFragment) fragmentManager.findFragmentByTag(CONTACTS_TAG); @@ -847,6 +886,10 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen @Override public void onVoicemailSelected() { + if (selectedTab != TabIndex.VOICEMAIL) { + Logger.get(context).logImpression(DialerImpression.Type.NUI_SWITCH_TAB_TO_VOICEMAIL); + selectedTab = TabIndex.VOICEMAIL; + } hideAllFragments(); VisualVoicemailCallLogFragment fragment = (VisualVoicemailCallLogFragment) fragmentManager.findFragmentByTag(VOICEMAIL_TAG); diff --git a/java/com/android/dialer/metrics/Metrics.java b/java/com/android/dialer/metrics/Metrics.java new file mode 100644 index 000000000..3922a8cfa --- /dev/null +++ b/java/com/android/dialer/metrics/Metrics.java @@ -0,0 +1,39 @@ +/* + * 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.metrics; + +import android.app.Application; +import android.content.Context; + +/** Logs metrics. */ +public interface Metrics { + + /** Start a timer. */ + void startTimer(Context context, String timerEventName); + + /** Stop a timer. */ + void stopTimer(String timerEventName); + + /** Record memory. */ + void recordMemory(String memoryEventName); + + /** Initiazer for metrics. */ + interface Initializer { + /** Initialize metrics for the application . */ + void initialize(Application application); + } +} diff --git a/java/com/android/dialer/metrics/MetricsComponent.java b/java/com/android/dialer/metrics/MetricsComponent.java new file mode 100644 index 000000000..f37129791 --- /dev/null +++ b/java/com/android/dialer/metrics/MetricsComponent.java @@ -0,0 +1,41 @@ +/* + * 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.metrics; + +import android.content.Context; +import com.android.dialer.inject.HasRootComponent; +import dagger.Subcomponent; + +/** Component for metrics. */ +@Subcomponent +public abstract class MetricsComponent { + + public abstract Metrics metrics(); + + public abstract Metrics.Initializer metricsInitializer(); + + public static MetricsComponent get(Context context) { + return ((MetricsComponent.HasComponent) + ((HasRootComponent) context.getApplicationContext()).component()) + .metricsComponent(); + } + + /** Used to refer to the root application component. */ + public interface HasComponent { + MetricsComponent metricsComponent(); + } +} diff --git a/java/com/android/dialer/metrics/StubMetrics.java b/java/com/android/dialer/metrics/StubMetrics.java new file mode 100644 index 000000000..114eb4308 --- /dev/null +++ b/java/com/android/dialer/metrics/StubMetrics.java @@ -0,0 +1,36 @@ +/* + * 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.metrics; + +import android.content.Context; +import javax.inject.Inject; + +/** Stub {@link Metrics}. */ +public final class StubMetrics implements Metrics { + + @Inject + StubMetrics() {} + + @Override + public void startTimer(Context context, String timerEventName) {} + + @Override + public void stopTimer(String timerEventName) {} + + @Override + public void recordMemory(String memoryEventName) {} +} diff --git a/java/com/android/dialer/metrics/StubMetricsInitializer.java b/java/com/android/dialer/metrics/StubMetricsInitializer.java new file mode 100644 index 000000000..cea408737 --- /dev/null +++ b/java/com/android/dialer/metrics/StubMetricsInitializer.java @@ -0,0 +1,30 @@ +/* + * 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.metrics; + +import android.app.Application; +import javax.inject.Inject; + +/** Stub for {@link Metrics.Initializer}. */ +public class StubMetricsInitializer implements Metrics.Initializer { + + @Inject + StubMetricsInitializer() {} + + @Override + public void initialize(Application application) {} +} diff --git a/java/com/android/dialer/metrics/StubMetricsModule.java b/java/com/android/dialer/metrics/StubMetricsModule.java new file mode 100644 index 000000000..a2d9ebfe2 --- /dev/null +++ b/java/com/android/dialer/metrics/StubMetricsModule.java @@ -0,0 +1,31 @@ +/* + * 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.metrics; + +import dagger.Binds; +import dagger.Module; + +/** Binds stub {@link Metrics}. */ +@Module +public interface StubMetricsModule { + + @Binds + Metrics bindMetrics(StubMetrics stub); + + @Binds + Metrics.Initializer bindMetricsInitializer(StubMetricsInitializer stub); +} diff --git a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java index ce4030d70..9c5411081 100644 --- a/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java +++ b/java/com/android/dialer/phonelookup/consolidator/PhoneLookupInfoConsolidator.java @@ -108,6 +108,28 @@ public final class PhoneLookupInfoConsolidator { /** * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method + * returns the photo thumbnail URI associated with that number. + * + * <p>If no photo thumbnail URI can be obtained from the {@link PhoneLookupInfo}, an empty string + * will be returned. + */ + public String getPhotoThumbnailUri() { + switch (nameSource) { + case NameSource.CP2_LOCAL: + return Assert.isNotNull(firstCp2LocalContact).getPhotoThumbnailUri(); + case NameSource.CP2_REMOTE: + return Assert.isNotNull(firstCp2RemoteContact).getPhotoThumbnailUri(); + case NameSource.PEOPLE_API: + case NameSource.NONE: + return ""; + default: + throw Assert.createUnsupportedOperationFailException( + String.format("Unsupported name source: %s", nameSource)); + } + } + + /** + * The {@link PhoneLookupInfo} passed to the constructor is associated with a number. This method * returns the photo URI associated with that number. * * <p>If no photo URI can be obtained from the {@link PhoneLookupInfo}, an empty string will be diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java b/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java index e3929990e..5a211eddc 100644 --- a/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java +++ b/java/com/android/dialer/phonelookup/cp2/Cp2Projections.java @@ -35,12 +35,13 @@ final class Cp2Projections { new String[] { Phone.DISPLAY_NAME_PRIMARY, // 0 Phone.PHOTO_THUMBNAIL_URI, // 1 - Phone.PHOTO_ID, // 2 - Phone.TYPE, // 3 - Phone.LABEL, // 4 - Phone.NORMALIZED_NUMBER, // 5 - Phone.CONTACT_ID, // 6 - Phone.LOOKUP_KEY // 7 + Phone.PHOTO_URI, // 2 + Phone.PHOTO_ID, // 3 + Phone.TYPE, // 4 + Phone.LABEL, // 5 + Phone.NORMALIZED_NUMBER, // 6 + Phone.CONTACT_ID, // 7 + Phone.LOOKUP_KEY // 8 }; // Projection for performing lookups using the PHONE_LOOKUP table @@ -48,23 +49,25 @@ final class Cp2Projections { new String[] { PhoneLookup.DISPLAY_NAME_PRIMARY, // 0 PhoneLookup.PHOTO_THUMBNAIL_URI, // 1 - PhoneLookup.PHOTO_ID, // 2 - PhoneLookup.TYPE, // 3 - PhoneLookup.LABEL, // 4 - PhoneLookup.NORMALIZED_NUMBER, // 5 - PhoneLookup.CONTACT_ID, // 6 - PhoneLookup.LOOKUP_KEY // 7 + PhoneLookup.PHOTO_URI, // 2 + PhoneLookup.PHOTO_ID, // 3 + PhoneLookup.TYPE, // 4 + PhoneLookup.LABEL, // 5 + PhoneLookup.NORMALIZED_NUMBER, // 6 + PhoneLookup.CONTACT_ID, // 7 + PhoneLookup.LOOKUP_KEY // 8 }; // The following indexes should match both PHONE_PROJECTION and PHONE_LOOKUP_PROJECTION above. private static final int CP2_INFO_NAME_INDEX = 0; - private static final int CP2_INFO_PHOTO_URI_INDEX = 1; - private static final int CP2_INFO_PHOTO_ID_INDEX = 2; - private static final int CP2_INFO_TYPE_INDEX = 3; - private static final int CP2_INFO_LABEL_INDEX = 4; - private static final int CP2_INFO_NORMALIZED_NUMBER_INDEX = 5; - private static final int CP2_INFO_CONTACT_ID_INDEX = 6; - private static final int CP2_INFO_LOOKUP_KEY_INDEX = 7; + private static final int CP2_INFO_PHOTO_THUMBNAIL_URI_INDEX = 1; + private static final int CP2_INFO_PHOTO_URI_INDEX = 2; + private static final int CP2_INFO_PHOTO_ID_INDEX = 3; + private static final int CP2_INFO_TYPE_INDEX = 4; + private static final int CP2_INFO_LABEL_INDEX = 5; + private static final int CP2_INFO_NORMALIZED_NUMBER_INDEX = 6; + private static final int CP2_INFO_CONTACT_ID_INDEX = 7; + private static final int CP2_INFO_LOOKUP_KEY_INDEX = 8; private Cp2Projections() {} @@ -82,6 +85,7 @@ final class Cp2Projections { */ static Cp2ContactInfo buildCp2ContactInfoFromCursor(Context appContext, Cursor cursor) { String displayName = cursor.getString(CP2_INFO_NAME_INDEX); + String photoThumbnailUri = cursor.getString(CP2_INFO_PHOTO_THUMBNAIL_URI_INDEX); String photoUri = cursor.getString(CP2_INFO_PHOTO_URI_INDEX); int photoId = cursor.getInt(CP2_INFO_PHOTO_ID_INDEX); int type = cursor.getInt(CP2_INFO_TYPE_INDEX); @@ -93,6 +97,9 @@ final class Cp2Projections { if (!TextUtils.isEmpty(displayName)) { infoBuilder.setName(displayName); } + if (!TextUtils.isEmpty(photoThumbnailUri)) { + infoBuilder.setPhotoThumbnailUri(photoThumbnailUri); + } if (!TextUtils.isEmpty(photoUri)) { infoBuilder.setPhotoUri(photoUri); } diff --git a/java/com/android/dialer/phonelookup/phone_lookup_info.proto b/java/com/android/dialer/phonelookup/phone_lookup_info.proto index e9cb9f8ad..dd6bf664c 100644 --- a/java/com/android/dialer/phonelookup/phone_lookup_info.proto +++ b/java/com/android/dialer/phonelookup/phone_lookup_info.proto @@ -19,6 +19,7 @@ message PhoneLookupInfo { message Cp2Info { // Information about a single contact, which can be a local contact or a // remote one. + // Next ID: 8 message Cp2ContactInfo { // For a local contact: // android.provider.ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY @@ -30,13 +31,19 @@ message PhoneLookupInfo { // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_THUMBNAIL_URI // For a remote contact: // android.provider.ContactsContract.PhoneLookup.PHOTO_THUMBNAIL_URI - optional string photo_uri = 2; + optional string photo_thumbnail_uri = 2; + + // For a local contact: + // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_URI + // For a remote contact: + // android.provider.ContactsContract.PhoneLookup.PHOTO_URI + optional string photo_uri = 3; // For a local contact: // android.provider.ContactsContract.CommonDataKinds.Phone.PHOTO_ID // For a remote contact: // android.provider.ContactsContract.PhoneLookup.PHOTO_ID - optional fixed64 photo_id = 3; + optional fixed64 photo_id = 4; // For a local contact: // android.provider.ContactsContract.CommonDataKinds.Phone.LABEL @@ -44,13 +51,13 @@ message PhoneLookupInfo { // android.provider.ContactsContract.PhoneLookup.LABEL // // The value can be "Home", "Mobile", ect. - optional string label = 4; + optional string label = 5; // For a local contact: // android.provider.ContactsContract.CommonDataKinds.Phone.CONTACT_ID // For a remote contact: // android.provider.ContactsContract.PhoneLookup.CONTACT_ID - optional fixed64 contact_id = 5; + optional fixed64 contact_id = 6; // For a local contact: // constructed based on @@ -58,7 +65,7 @@ message PhoneLookupInfo { // For a remote contact: // constructed based on // android.provider.ContactsContract.PhoneLookup.LOOKUP_KEY - optional string lookup_uri = 6; + optional string lookup_uri = 7; } // Repeated because one phone number can be associated with multiple CP2 // contacts. diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index c62d40e59..2d45457d2 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -488,7 +488,9 @@ public final class NewSearchFragment extends Fragment if (event.getAction() == MotionEvent.ACTION_UP) { v.performClick(); } - FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onSearchListTouch(); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + FragmentUtils.getParentUnsafe(this, SearchFragmentListener.class).onSearchListTouch(); + } return false; } diff --git a/java/com/android/dialer/simulator/Simulator.java b/java/com/android/dialer/simulator/Simulator.java index 3c2526be7..11a07d974 100644 --- a/java/com/android/dialer/simulator/Simulator.java +++ b/java/com/android/dialer/simulator/Simulator.java @@ -101,6 +101,9 @@ public interface Simulator { MERGE, SEPARATE, SWAP, + START_RTT, + STOP_RTT, + HANDLE_RTT_UPGRADE_RESPONSE, }) public @interface Type {} @@ -118,6 +121,9 @@ public interface Simulator { public static final int MERGE = 11; public static final int SEPARATE = 12; public static final int SWAP = 13; + public static final int START_RTT = 14; + public static final int STOP_RTT = 15; + public static final int HANDLE_RTT_UPGRADE_RESPONSE = 16; @Type public final int type; /** Holds event specific information. For example, for DTMF this could be the keycode. */ diff --git a/java/com/android/dialer/simulator/impl/RttChatBot.java b/java/com/android/dialer/simulator/impl/RttChatBot.java new file mode 100644 index 000000000..9c2989a07 --- /dev/null +++ b/java/com/android/dialer/simulator/impl/RttChatBot.java @@ -0,0 +1,139 @@ +/* + * 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.simulator.impl; + +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.support.annotation.MainThread; +import android.telecom.Connection.RttTextStream; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.incallui.rtt.protocol.Constants; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** Chat bot to generate remote RTT chat messages. */ +public class RttChatBot { + + interface Callback { + void type(String text); + } + + private static final int START_SENDING = 1; + private static final int SEND_MESSAGE = 2; + + private static final String[] CANDIDATE_MESSAGES = + new String[] { + "To RTT or not to RTT, that is the question...", + "Making TTY great again!", + "I would be more comfortable with real \"Thyme\" chatting." + + " I don't know how to end this pun", + "お疲れ様でした", + "The FCC has mandated that I respond... I will do so begrudgingly", + "😂😂😂💯" + }; + + private final MessageHandler messageHandler; + private final HandlerThread handlerThread; + + RttChatBot(RttTextStream rttTextStream) { + handlerThread = new HandlerThread("RttChatBot"); + handlerThread.start(); + messageHandler = new MessageHandler(handlerThread.getLooper(), rttTextStream); + } + + @MainThread + public void start() { + Assert.isMainThread(); + LogUtil.enterBlock("RttChatBot.start"); + messageHandler.sendEmptyMessage(START_SENDING); + } + + @MainThread + public void stop() { + Assert.isMainThread(); + LogUtil.enterBlock("RttChatBot.stop"); + if (handlerThread != null && handlerThread.isAlive()) { + handlerThread.quit(); + } + } + + private static class MessageHandler extends Handler { + private final RttTextStream rttTextStream; + private final Random random = new Random(); + private final List<String> messageQueue = new ArrayList<>(); + private int currentTypingPosition = -1; + private String currentTypingMessage = null; + + MessageHandler(Looper looper, RttTextStream rttTextStream) { + super(looper); + this.rttTextStream = rttTextStream; + } + + @Override + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case START_SENDING: + sendMessage(obtainMessage(SEND_MESSAGE, nextTyping())); + 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) { + LogUtil.e("RttChatBot.MessageHandler", "write message", e); + } + if (Constants.BUBBLE_BREAKER.equals(message)) { + // Wait 1-11s between two messages. + sendMessageDelayed( + obtainMessage(SEND_MESSAGE, nextTyping()), 1000 * (1 + random.nextInt(10))); + } else { + // Wait up to 2s between typing. + sendMessageDelayed(obtainMessage(SEND_MESSAGE, nextTyping()), 200 * random.nextInt(10)); + } + break; + default: // fall out + } + } + + private String nextTyping() { + if (currentTypingPosition < 0 || currentTypingMessage == null) { + if (messageQueue.isEmpty()) { + String text = CANDIDATE_MESSAGES[random.nextInt(CANDIDATE_MESSAGES.length)]; + messageQueue.add(text); + } + currentTypingMessage = messageQueue.remove(0); + currentTypingPosition = 0; + } + if (currentTypingPosition < currentTypingMessage.length()) { + int size = random.nextInt(currentTypingMessage.length() - currentTypingPosition + 1); + String messageToType = + currentTypingMessage.substring(currentTypingPosition, currentTypingPosition + size); + currentTypingPosition = currentTypingPosition + size; + return messageToType; + } else { + currentTypingPosition = -1; + currentTypingMessage = null; + return Constants.BUBBLE_BREAKER; + } + } + } +} diff --git a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java index 2bfa98247..81a3d30de 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConferenceCreator.java @@ -97,7 +97,8 @@ final class SimulatorConferenceCreator default: break; } - SimulatorSimCallManager.addNewIncomingCall(context, number, false /* isVideo */, extras); + SimulatorSimCallManager.addNewIncomingCall( + context, number, SimulatorSimCallManager.CALL_TYPE_VOICE, extras); } @Override diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java index d7427dd12..c832a5051 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java +++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java @@ -121,6 +121,24 @@ public final class SimulatorConnection extends Connection { onEvent(new Event(Event.DTMF, Character.toString(c), null)); } + @Override + public void onStartRtt(@NonNull RttTextStream rttTextStream) { + LogUtil.enterBlock("SimulatorConnection.onStartRtt"); + onEvent(new Event(Event.START_RTT)); + } + + @Override + public void onStopRtt() { + LogUtil.enterBlock("SimulatorConnection.onStopRtt"); + onEvent(new Event(Event.STOP_RTT)); + } + + @Override + public void handleRttUpgradeResponse(RttTextStream rttTextStream) { + LogUtil.enterBlock("SimulatorConnection.handleRttUpgradeResponse"); + onEvent(new Event(Event.HANDLE_RTT_UPGRADE_RESPONSE)); + } + void onEvent(@NonNull Event event) { events.add(Assert.isNotNull(event)); for (Listener listener : new ArrayList<>(listeners)) { diff --git a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java index 0bd1c0f22..1bf4b2a00 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java +++ b/java/com/android/dialer/simulator/impl/SimulatorMainMenu.java @@ -33,7 +33,6 @@ import com.android.dialer.enrichedcall.simulator.EnrichedCallSimulatorActivity; import com.android.dialer.persistentlog.PersistentLogger; import com.android.dialer.preferredsim.PreferredSimFallbackContract; import com.android.dialer.simulator.SimulatorComponent; -import com.android.incallui.rtt.impl.RttChatActivity; /** Implements the top level simulator menu. */ final class SimulatorMainMenu { @@ -42,9 +41,9 @@ final class SimulatorMainMenu { SimulatorSubMenu simulatorSubMenu = new SimulatorSubMenu(activity.getApplicationContext()); simulatorSubMenu .addItem("Voice call", SimulatorVoiceCall.getActionProvider(activity)) + .addItem("Rtt call", SimulatorRttCall.getActionProvider(activity.getApplicationContext())) .addItem( "IMS video", SimulatorVideoCall.getActionProvider(activity.getApplicationContext())) - .addItem("Rtt call mock", () -> simulateRttCallMock(activity.getApplicationContext())) .addItem( "Notifications", SimulatorNotifications.getActionProvider(activity.getApplicationContext())) @@ -79,10 +78,6 @@ final class SimulatorMainMenu { return simulatorSubMenu; } - private static void simulateRttCallMock(@NonNull Context context) { - context.startActivity(new Intent(context, RttChatActivity.class)); - } - private static void populateDatabase(@NonNull Context context) { DialerExecutorComponent.get(context) .dialerExecutorFactory() diff --git a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java index 6d4a26278..b8556156b 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java +++ b/java/com/android/dialer/simulator/impl/SimulatorMissedCallCreator.java @@ -78,7 +78,8 @@ final class SimulatorMissedCallCreator implements SimulatorConnectionService.Lis extras.putInt(EXTRA_CALL_COUNT, callCount - 1); extras.putBoolean(EXTRA_IS_MISSED_CALL_CONNECTION, true); - SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */, extras); + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras); } private static boolean isMissedCallConnection(@NonNull Connection connection) { diff --git a/java/com/android/dialer/simulator/impl/SimulatorRttCall.java b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java new file mode 100644 index 000000000..7b0066719 --- /dev/null +++ b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2017 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.simulator.impl; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.telecom.Connection; +import android.telecom.DisconnectCause; +import android.view.ActionProvider; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.dialer.simulator.Simulator; +import com.android.dialer.simulator.Simulator.Event; + +/** Entry point in the simulator to create voice calls. */ +final class SimulatorRttCall + implements SimulatorConnectionService.Listener, SimulatorConnection.Listener { + + @NonNull private final Context context; + @Nullable private String connectionTag; + + static ActionProvider getActionProvider(@NonNull Context context) { + return new SimulatorSubMenu(context) + .addItem("Incoming call", () -> new SimulatorRttCall(context).addNewIncomingCall(false)) + .addItem("Outgoing call", () -> new SimulatorRttCall(context).addNewOutgoingCall()) + .addItem("Emergency call", () -> new SimulatorRttCall(context).addNewEmergencyCall()); + } + + private SimulatorRttCall(@NonNull Context context) { + this.context = Assert.isNotNull(context); + SimulatorConnectionService.addListener(this); + SimulatorConnectionService.addListener( + new SimulatorConferenceCreator(context, Simulator.CONFERENCE_TYPE_GSM)); + } + + private void addNewIncomingCall(boolean isSpam) { + String callerId = + isSpam + ? "+1-661-778-3020" /* Blacklisted custom spam number */ + : "+44 (0) 20 7031 3000" /* Google London office */; + connectionTag = + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_RTT); + } + + private void addNewOutgoingCall() { + String callerId = "+55-31-2128-6800"; // Brazil office. + connectionTag = + SimulatorSimCallManager.addNewOutgoingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_RTT); + } + + private void addNewEmergencyCall() { + String callerId = "911"; + connectionTag = + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_RTT); + } + + @Override + public void onNewOutgoingConnection(@NonNull SimulatorConnection connection) { + if (isMyConnection(connection)) { + LogUtil.i("SimulatorRttCall.onNewOutgoingConnection", "connection created"); + handleNewConnection(connection); + + // Telecom will force the connection to switch to Dialing when we return it. Wait until after + // we're returned it before changing call state. + ThreadUtil.postOnUiThread(connection::setActive); + } + } + + @Override + public void onNewIncomingConnection(@NonNull SimulatorConnection connection) { + if (isMyConnection(connection)) { + LogUtil.i("SimulatorRttCall.onNewIncomingConnection", "connection created"); + handleNewConnection(connection); + } + } + + @Override + public void onConference( + @NonNull SimulatorConnection connection1, @NonNull SimulatorConnection connection2) {} + + private void handleNewConnection(@NonNull SimulatorConnection connection) { + connection.addListener(this); + connection.setConnectionProperties( + connection.getConnectionProperties() | Connection.PROPERTY_IS_RTT); + } + + private boolean isMyConnection(@NonNull Connection connection) { + return connection.getExtras().getBoolean(connectionTag); + } + + @Override + public void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event) { + 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.UNHOLD: + connection.setActive(); + break; + case Event.DISCONNECT: + connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL)); + break; + case Event.SESSION_MODIFY_REQUEST: + ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000); + 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 f28393c0c..d51e06816 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java +++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.telecom.Connection; import android.telecom.ConnectionRequest; @@ -30,6 +31,8 @@ import android.telephony.TelephonyManager; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.strictmode.StrictModeUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; import java.util.Random; @@ -46,10 +49,20 @@ import java.util.Random; */ public class SimulatorSimCallManager { + public static final int CALL_TYPE_VOICE = 1; + public static final int CALL_TYPE_VIDEO = 2; + public static final int CALL_TYPE_RTT = 3; + + /** Call type of a simulator call. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({CALL_TYPE_VOICE, CALL_TYPE_VIDEO, CALL_TYPE_RTT}) + public @interface CallType {} + private static final String SIM_CALL_MANAGER_ACCOUNT_ID = "SIMULATOR_ACCOUNT_ID"; private static final String VIDEO_PROVIDER_ACCOUNT_ID = "SIMULATOR_VIDEO_ACCOUNT_ID"; private static final String EXTRA_IS_SIMULATOR_CONNECTION = "is_simulator_connection"; private static final String EXTRA_CONNECTION_TAG = "connection_tag"; + private static final String EXTRA_CONNECTION_CALL_TYPE = "connection_call_type"; static void register(@NonNull Context context) { LogUtil.enterBlock("SimulatorSimCallManager.register"); @@ -75,15 +88,15 @@ public class SimulatorSimCallManager { @NonNull public static String addNewOutgoingCall( - @NonNull Context context, @NonNull String phoneNumber, boolean isVideo) { - return addNewOutgoingCall(context, phoneNumber, isVideo, new Bundle()); + @NonNull Context context, @NonNull String phoneNumber, @CallType int callType) { + return addNewOutgoingCall(context, phoneNumber, callType, new Bundle()); } @NonNull public static String addNewOutgoingCall( @NonNull Context context, @NonNull String phoneNumber, - boolean isVideo, + @CallType int callType, @NonNull Bundle extras) { LogUtil.enterBlock("SimulatorSimCallManager.addNewOutgoingCall"); Assert.isNotNull(context); @@ -94,13 +107,18 @@ public class SimulatorSimCallManager { register(context); extras = new Bundle(extras); - extras.putAll(createSimulatorConnectionExtras()); + extras.putAll(createSimulatorConnectionExtras(callType)); Bundle outgoingCallExtras = new Bundle(); outgoingCallExtras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); outgoingCallExtras.putParcelable( TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, - isVideo ? getVideoProviderHandle(context) : getSystemPhoneAccountHandle(context)); + callType == CALL_TYPE_VIDEO + ? getVideoProviderHandle(context) + : getSystemPhoneAccountHandle(context)); + if (callType == CALL_TYPE_RTT) { + outgoingCallExtras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true); + } TelecomManager telecomManager = context.getSystemService(TelecomManager.class); try { @@ -114,13 +132,16 @@ public class SimulatorSimCallManager { @NonNull public static String addNewIncomingCall( - @NonNull Context context, @NonNull String callerId, boolean isVideo) { - return addNewIncomingCall(context, callerId, isVideo, new Bundle()); + @NonNull Context context, @NonNull String callerId, @CallType int callType) { + return addNewIncomingCall(context, callerId, callType, new Bundle()); } @NonNull public static String addNewIncomingCall( - @NonNull Context context, @NonNull String callerId, boolean isVideo, @NonNull Bundle extras) { + @NonNull Context context, + @NonNull String callerId, + @CallType int callType, + @NonNull Bundle extras) { LogUtil.enterBlock("SimulatorSimCallManager.addNewIncomingCall"); Assert.isNotNull(context); Assert.isNotNull(callerId); @@ -130,18 +151,21 @@ public class SimulatorSimCallManager { extras = new Bundle(extras); extras.putString(TelephonyManager.EXTRA_INCOMING_NUMBER, callerId); - extras.putAll(createSimulatorConnectionExtras()); + extras.putAll(createSimulatorConnectionExtras(callType)); TelecomManager telecomManager = context.getSystemService(TelecomManager.class); telecomManager.addNewIncomingCall( - isVideo ? getVideoProviderHandle(context) : getSystemPhoneAccountHandle(context), extras); + callType == CALL_TYPE_VIDEO + ? getVideoProviderHandle(context) + : getSystemPhoneAccountHandle(context), + extras); return extras.getString(EXTRA_CONNECTION_TAG); } @NonNull private static PhoneAccount buildSimCallManagerAccount(Context context) { return new PhoneAccount.Builder(getSimCallManagerHandle(context), "Simulator SIM call manager") - .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER) + .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER | PhoneAccount.CAPABILITY_RTT) .setShortDescription("Simulator SIM call manager") .setSupportedUriSchemes(Arrays.asList(PhoneAccount.SCHEME_TEL)) .build(); @@ -218,12 +242,16 @@ public class SimulatorSimCallManager { } @NonNull - static Bundle createSimulatorConnectionExtras() { + static Bundle createSimulatorConnectionExtras(@CallType int callType) { Bundle extras = new Bundle(); extras.putBoolean(EXTRA_IS_SIMULATOR_CONNECTION, true); String connectionTag = createUniqueConnectionTag(); extras.putString(EXTRA_CONNECTION_TAG, connectionTag); extras.putBoolean(connectionTag, true); + extras.putInt(EXTRA_CONNECTION_CALL_TYPE, callType); + if (callType == CALL_TYPE_RTT) { + extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true); + } return extras; } diff --git a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java index f7256a11c..0bb56f1f9 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVideoCall.java @@ -77,7 +77,8 @@ final class SimulatorVideoCall } String callerId = "+44 (0) 20 7031 3000"; // Google London office connectionTag = - SimulatorSimCallManager.addNewIncomingCall(context, callerId, true /* isVideo */); + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VIDEO); } private void addNewOutgoingCall() { @@ -87,7 +88,8 @@ final class SimulatorVideoCall } String phoneNumber = "+44 (0) 20 7031 3000"; // Google London office connectionTag = - SimulatorSimCallManager.addNewOutgoingCall(context, phoneNumber, true /* isVideo */); + SimulatorSimCallManager.addNewOutgoingCall( + context, phoneNumber, SimulatorSimCallManager.CALL_TYPE_VIDEO); } @Override diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java index 67a2db804..d4c7ee458 100644 --- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java +++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java @@ -104,7 +104,10 @@ final class SimulatorVoiceCall extras.putBoolean(Simulator.IS_ENRICHED_CALL, true); connectionTag = SimulatorSimCallManager.addNewIncomingCall( - context, Simulator.ENRICHED_CALL_INCOMING_NUMBER, false, extras); + context, + Simulator.ENRICHED_CALL_INCOMING_NUMBER, + SimulatorSimCallManager.CALL_TYPE_VOICE, + extras); }, DialerExecutorComponent.get(context).uiExecutor()); } @@ -119,7 +122,10 @@ final class SimulatorVoiceCall extras.putBoolean(Simulator.IS_ENRICHED_CALL, true); connectionTag = SimulatorSimCallManager.addNewOutgoingCall( - context, Simulator.ENRICHED_CALL_OUTGOING_NUMBER, false, extras); + context, + Simulator.ENRICHED_CALL_OUTGOING_NUMBER, + SimulatorSimCallManager.CALL_TYPE_VOICE, + extras); }, DialerExecutorComponent.get(context).uiExecutor()); } @@ -127,7 +133,8 @@ final class SimulatorVoiceCall private void addNewIncomingCall() { String callerId = "+44 (0) 20 7031 3000" /* Google London office */; connectionTag = - SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */); + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE); } private void addNewIncomingCall(AppCompatActivity activity) { @@ -137,7 +144,7 @@ final class SimulatorVoiceCall extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation); connectionTag = SimulatorSimCallManager.addNewIncomingCall( - context, callerId, false /* isVideo */, extras); + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras); }) .show(activity.getSupportFragmentManager(), "SimulatorDialog"); } @@ -145,7 +152,8 @@ final class SimulatorVoiceCall private void addNewOutgoingCall() { String callerId = "+55-31-2128-6800"; // Brazil office. connectionTag = - SimulatorSimCallManager.addNewOutgoingCall(context, callerId, false /* isVideo */); + SimulatorSimCallManager.addNewOutgoingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE); } private void addNewOutgoingCall(AppCompatActivity activity) { @@ -155,7 +163,7 @@ final class SimulatorVoiceCall extras.putInt(Simulator.PRESENTATION_CHOICE, callerIdPresentation); connectionTag = SimulatorSimCallManager.addNewOutgoingCall( - context, callerId, false /* isVideo */, extras); + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE, extras); }) .show(activity.getSupportFragmentManager(), "SimulatorDialog"); } @@ -163,12 +171,15 @@ final class SimulatorVoiceCall private void addSpamIncomingCall() { String callerId = "+1-661-778-3020"; /* Blacklisted custom spam number */ connectionTag = - SimulatorSimCallManager.addNewIncomingCall(context, callerId, false /* isVideo */); + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE); } private void addNewEmergencyCallBack() { String callerId = "911"; - connectionTag = SimulatorSimCallManager.addNewIncomingCall(context, callerId, false); + connectionTag = + SimulatorSimCallManager.addNewIncomingCall( + context, callerId, SimulatorSimCallManager.CALL_TYPE_VOICE); } @Override diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java index 8769be5d9..67f5cfe4f 100644 --- a/java/com/android/incallui/InCallActivity.java +++ b/java/com/android/incallui/InCallActivity.java @@ -83,6 +83,10 @@ import com.android.incallui.incall.protocol.InCallScreen; import com.android.incallui.incall.protocol.InCallScreenDelegate; import com.android.incallui.incall.protocol.InCallScreenDelegateFactory; import com.android.incallui.incalluilock.InCallUiLock; +import com.android.incallui.rtt.bindings.RttBindings; +import com.android.incallui.rtt.protocol.RttCallScreen; +import com.android.incallui.rtt.protocol.RttCallScreenDelegate; +import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory; import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment; import com.android.incallui.video.bindings.VideoBindings; import com.android.incallui.video.protocol.VideoCallScreen; @@ -100,6 +104,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity InCallScreenDelegateFactory, InCallButtonUiDelegateFactory, VideoCallScreenDelegateFactory, + RttCallScreenDelegateFactory, PseudoScreenState.StateChangedListener { @Retention(RetentionPolicy.SOURCE) @@ -136,6 +141,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity private boolean didShowAnswerScreen; private boolean didShowInCallScreen; private boolean didShowVideoCallScreen; + private boolean didShowRttCallScreen; private boolean dismissKeyguard; private boolean isInShowMainInCallFragment; private boolean isRecreating; // whether the activity is going to be recreated @@ -1220,37 +1226,47 @@ public class InCallActivity extends TransactionSafeFragmentActivity isInShowMainInCallFragment = true; ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi(); ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi(); + ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi(); LogUtil.i( "InCallActivity.showMainInCallFragment", - "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, " - + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b", + "shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b " + + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowRttCallScreen: %b, " + + "didShowVideoCallScreen: %b", shouldShowAnswerUi.shouldShow, + shouldShowRttUi.shouldShow, shouldShowVideoUi.shouldShow, didShowAnswerScreen, didShowInCallScreen, + didShowRttCallScreen, didShowVideoCallScreen); // Only video call ui allows orientation change. setAllowOrientationChange(shouldShowVideoUi.shouldShow); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - boolean didChangeInCall; - boolean didChangeVideo; - boolean didChangeAnswer; + boolean didChange; if (shouldShowAnswerUi.shouldShow) { - didChangeInCall = hideInCallScreenFragment(transaction); - didChangeVideo = hideVideoCallScreenFragment(transaction); - didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call); + didChange = hideInCallScreenFragment(transaction); + didChange |= hideVideoCallScreenFragment(transaction); + didChange |= hideRttCallScreenFragment(transaction); + didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call); } else if (shouldShowVideoUi.shouldShow) { - didChangeInCall = hideInCallScreenFragment(transaction); - didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call); - didChangeAnswer = hideAnswerScreenFragment(transaction); + didChange = hideInCallScreenFragment(transaction); + didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call); + didChange |= hideRttCallScreenFragment(transaction); + didChange |= hideAnswerScreenFragment(transaction); + } else if (shouldShowRttUi.shouldShow) { + didChange = hideInCallScreenFragment(transaction); + didChange |= hideVideoCallScreenFragment(transaction); + didChange |= hideAnswerScreenFragment(transaction); + didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call); } else { - didChangeInCall = showInCallScreenFragment(transaction); - didChangeVideo = hideVideoCallScreenFragment(transaction); - didChangeAnswer = hideAnswerScreenFragment(transaction); + didChange = showInCallScreenFragment(transaction); + didChange |= hideVideoCallScreenFragment(transaction); + didChange |= hideRttCallScreenFragment(transaction); + didChange |= hideAnswerScreenFragment(transaction); } - if (didChangeInCall || didChangeVideo || didChangeAnswer) { + if (didChange) { Trace.beginSection("InCallActivity.commitTransaction"); transaction.commitNow(); Trace.endSection(); @@ -1308,6 +1324,26 @@ public class InCallActivity extends TransactionSafeFragmentActivity return new ShouldShowUiResult(false, null); } + private static ShouldShowUiResult getShouldShowRttUi() { + DialerCall call = CallList.getInstance().getFirstCall(); + if (call == null) { + LogUtil.i("InCallActivity.getShouldShowRttUi", "null call"); + return new ShouldShowUiResult(false, null); + } + + if (call.isRttCall()) { + LogUtil.i("InCallActivity.getShouldShowRttUi", "found rtt call"); + return new ShouldShowUiResult(true, call); + } + + if (call.hasSentRttUpgradeRequest()) { + LogUtil.i("InCallActivity.getShouldShowRttUi", "upgrading to rtt"); + return new ShouldShowUiResult(true, call); + } + + return new ShouldShowUiResult(false, null); + } + private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) { // When rejecting a call the active call can become null in which case we should continue // showing the answer screen. @@ -1347,6 +1383,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity AnswerScreen answerScreen = AnswerBindings.createAnswerScreen( call.getId(), + call.isRttCall(), call.isVideoCall(), isVideoUpgradeRequest, call.getVideoTech().isSelfManagedCamera(), @@ -1418,6 +1455,33 @@ public class InCallActivity extends TransactionSafeFragmentActivity return true; } + private boolean showRttCallScreenFragment(FragmentTransaction transaction, DialerCall call) { + if (didShowRttCallScreen) { + // This shouldn't happen since only one RTT call is allow at same time. + if (!getRttCallScreen().getCallId().equals(call.getId())) { + LogUtil.e("InCallActivity.showRttCallScreenFragment", "RTT call id doesn't match"); + } + return false; + } + RttCallScreen rttCallScreen = RttBindings.createRttCallScreen(call.getId()); + transaction.add(R.id.main, rttCallScreen.getRttCallScreenFragment(), Tags.RTT_CALL_SCREEN); + Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this); + didShowRttCallScreen = true; + return true; + } + + private boolean hideRttCallScreenFragment(FragmentTransaction transaction) { + if (!didShowRttCallScreen) { + return false; + } + RttCallScreen rttCallScreen = getRttCallScreen(); + if (rttCallScreen != null) { + transaction.remove(rttCallScreen.getRttCallScreenFragment()); + } + didShowRttCallScreen = false; + return true; + } + private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) { if (didShowVideoCallScreen) { VideoCallScreen videoCallScreen = getVideoCallScreen(); @@ -1467,6 +1531,10 @@ public class InCallActivity extends TransactionSafeFragmentActivity return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN); } + private RttCallScreen getRttCallScreen() { + return (RttCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.RTT_CALL_SCREEN); + } + @Override public void onPseudoScreenStateChanged(boolean isOn) { LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn); @@ -1499,6 +1567,11 @@ public class InCallActivity extends TransactionSafeFragmentActivity return super.dispatchTouchEvent(event); } + @Override + public RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen videoCallScreen) { + return new RttCallPresenter(); + } + private static class ShouldShowUiResult { public final boolean shouldShow; public final DialerCall call; @@ -1536,6 +1609,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi"; static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment"; static final String VIDEO_CALL_SCREEN = "tag_video_call_screen"; + static final String RTT_CALL_SCREEN = "tag_rtt_call_screen"; static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment"; } diff --git a/java/com/android/incallui/ProximitySensor.java b/java/com/android/incallui/ProximitySensor.java index f82b75d06..4b033441d 100644 --- a/java/com/android/incallui/ProximitySensor.java +++ b/java/com/android/incallui/ProximitySensor.java @@ -55,6 +55,7 @@ public class ProximitySensor private boolean dialpadVisible; private boolean isAttemptingVideoCall; private boolean isVideoCall; + private boolean isRttCall; public ProximitySensor( @NonNull Context context, @@ -112,10 +113,14 @@ public class ProximitySensor DialerCall activeCall = callList.getActiveCall(); boolean isVideoCall = activeCall != null && activeCall.isVideoCall(); + boolean isRttCall = activeCall != null && activeCall.isRttCall(); - if (isOffhook != isPhoneOffhook || this.isVideoCall != isVideoCall) { + if (isOffhook != isPhoneOffhook + || this.isVideoCall != isVideoCall + || this.isRttCall != isRttCall) { isPhoneOffhook = isOffhook; this.isVideoCall = isVideoCall; + this.isRttCall = isRttCall; orientation = AccelerometerListener.ORIENTATION_UNKNOWN; accelerometerListener.enable(isPhoneOffhook); @@ -217,7 +222,8 @@ public class ProximitySensor || CallAudioState.ROUTE_SPEAKER == audioRoute || CallAudioState.ROUTE_BLUETOOTH == audioRoute || isAttemptingVideoCall - || isVideoCall); + || isVideoCall + || isRttCall); // We do not keep the screen off when the user is outside in-call screen and we are // horizontal, but we do not force it on when we become horizontal until the diff --git a/java/com/android/incallui/RttCallPresenter.java b/java/com/android/incallui/RttCallPresenter.java new file mode 100644 index 000000000..b90d56b36 --- /dev/null +++ b/java/com/android/incallui/RttCallPresenter.java @@ -0,0 +1,43 @@ +/* + * 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; + +import android.content.Context; +import com.android.incallui.rtt.protocol.RttCallScreen; +import com.android.incallui.rtt.protocol.RttCallScreenDelegate; + +/** + * 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 { + + private Context appContext; + private RttCallScreen rttCallScreen; + + @Override + public void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen) { + this.appContext = context.getApplicationContext(); + this.rttCallScreen = rttCallScreen; + } + + @Override + public void onRttCallScreenUiReady() {} + + @Override + public void onRttCallScreenUiUnready() {} +} diff --git a/java/com/android/incallui/answer/bindings/AnswerBindings.java b/java/com/android/incallui/answer/bindings/AnswerBindings.java index 0b546db63..9f4199b7b 100644 --- a/java/com/android/incallui/answer/bindings/AnswerBindings.java +++ b/java/com/android/incallui/answer/bindings/AnswerBindings.java @@ -24,6 +24,7 @@ public class AnswerBindings { public static AnswerScreen createAnswerScreen( String callId, + boolean isRttCall, boolean isVideoCall, boolean isVideoUpgradeRequest, boolean isSelfManagedCamera, @@ -31,6 +32,7 @@ public class AnswerBindings { boolean hasCallOnHold) { return AnswerFragment.newInstance( callId, + isRttCall, isVideoCall, isVideoUpgradeRequest, isSelfManagedCamera, diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java index d687b6e01..8626e6d0e 100644 --- a/java/com/android/incallui/answer/impl/AnswerFragment.java +++ b/java/com/android/incallui/answer/impl/AnswerFragment.java @@ -103,6 +103,8 @@ public class AnswerFragment extends Fragment @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) static final String ARG_CALL_ID = "call_id"; + static final String ARG_IS_RTT_CALL = "is_rtt_call"; + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) static final String ARG_IS_VIDEO_CALL = "is_video_call"; @@ -344,6 +346,7 @@ public class AnswerFragment extends Fragment public static AnswerFragment newInstance( String callId, + boolean isRttCall, boolean isVideoCall, boolean isVideoUpgradeRequest, boolean isSelfManagedCamera, @@ -351,6 +354,7 @@ public class AnswerFragment extends Fragment boolean hasCallOnHold) { Bundle bundle = new Bundle(); bundle.putString(ARG_CALL_ID, Assert.isNotNull(callId)); + bundle.putBoolean(ARG_IS_RTT_CALL, isRttCall); bundle.putBoolean(ARG_IS_VIDEO_CALL, isVideoCall); bundle.putBoolean(ARG_IS_VIDEO_UPGRADE_REQUEST, isVideoUpgradeRequest); bundle.putBoolean(ARG_IS_SELF_MANAGED_CAMERA, isSelfManagedCamera); @@ -663,6 +667,7 @@ public class AnswerFragment extends Fragment Trace.beginSection("AnswerFragment.onCreateView"); Bundle arguments = getArguments(); Assert.checkState(arguments.containsKey(ARG_CALL_ID)); + Assert.checkState(arguments.containsKey(ARG_IS_RTT_CALL)); Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_CALL)); Assert.checkState(arguments.containsKey(ARG_IS_VIDEO_UPGRADE_REQUEST)); @@ -836,6 +841,11 @@ public class AnswerFragment extends Fragment } @Override + public boolean isRttCall() { + return getArguments().getBoolean(ARG_IS_RTT_CALL); + } + + @Override public boolean isVideoCall() { return getArguments().getBoolean(ARG_IS_VIDEO_CALL); } diff --git a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java index afa194f2e..0f1455c74 100644 --- a/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java +++ b/java/com/android/incallui/answer/impl/answermethod/AnswerMethodHolder.java @@ -46,4 +46,6 @@ public interface AnswerMethodHolder { boolean isVideoCall(); boolean isVideoUpgradeRequest(); + + boolean isRttCall(); } diff --git a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java index fe6bbbca5..b5dbc0c20 100644 --- a/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java +++ b/java/com/android/incallui/answer/impl/answermethod/FlingUpDownMethod.java @@ -335,6 +335,8 @@ public class FlingUpDownMethod extends AnswerMethod implements OnProgressChanged } if (getParent().isVideoCall() || getParent().isVideoUpgradeRequest()) { contactPuckIcon.setImageResource(R.drawable.quantum_ic_videocam_white_24); + } else if (getParent().isRttCall()) { + contactPuckIcon.setImageResource(R.drawable.quantum_ic_call_white_24); } else { contactPuckIcon.setImageResource(R.drawable.quantum_ic_call_white_24); } diff --git a/java/com/android/incallui/answer/protocol/AnswerScreen.java b/java/com/android/incallui/answer/protocol/AnswerScreen.java index 5ad500200..f030ce984 100644 --- a/java/com/android/incallui/answer/protocol/AnswerScreen.java +++ b/java/com/android/incallui/answer/protocol/AnswerScreen.java @@ -24,6 +24,8 @@ public interface AnswerScreen { String getCallId(); + boolean isRttCall(); + boolean isVideoCall(); boolean isVideoUpgradeRequest(); diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java index 1785eceb4..cbe7c57a6 100644 --- a/java/com/android/incallui/call/DialerCall.java +++ b/java/com/android/incallui/call/DialerCall.java @@ -32,6 +32,7 @@ import android.support.annotation.VisibleForTesting; import android.support.v4.os.BuildCompat; import android.telecom.Call; import android.telecom.Call.Details; +import android.telecom.Call.RttCall; import android.telecom.CallAudioState; import android.telecom.Connection; import android.telecom.DisconnectCause; @@ -263,6 +264,28 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa } @Override + public void onRttModeChanged(Call call, int mode) { + LogUtil.v("TelecomCallCallback.onRttModeChanged", "mode=%d", mode); + } + + @Override + public void onRttRequest(Call call, int id) { + LogUtil.v("TelecomCallCallback.onRttRequest", "id=%d", id); + } + + @Override + public void onRttInitiationFailure(Call call, int reason) { + LogUtil.v("TelecomCallCallback.onRttInitiationFailure", "reason=%d", reason); + update(); + } + + @Override + public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) { + LogUtil.v("TelecomCallCallback.onRttStatusChanged", "enabled=%b", enabled); + update(); + } + + @Override public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) { LogUtil.v( "TelecomCallCallback.onConnectionEvent", @@ -906,6 +929,14 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState()); } + public boolean isRttCall() { + if (BuildCompat.isAtLeastP()) { + return getTelecomCall().isRttActive(); + } else { + return false; + } + } + public boolean hasReceivedVideoUpgradeRequest() { return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState()); } @@ -914,6 +945,11 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState()); } + public boolean hasSentRttUpgradeRequest() { + // TODO(wangqi): Implement this. + return false; + } + /** * Determines if the call handle is an emergency number or not and caches the result to avoid * repeated calls to isEmergencyNumber. diff --git a/java/com/android/incallui/rtt/bindings/RttBindings.java b/java/com/android/incallui/rtt/bindings/RttBindings.java new file mode 100644 index 000000000..8f9a1437e --- /dev/null +++ b/java/com/android/incallui/rtt/bindings/RttBindings.java @@ -0,0 +1,28 @@ +/* + * 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.bindings; + +import com.android.incallui.rtt.impl.RttChatFragment; +import com.android.incallui.rtt.protocol.RttCallScreen; + +/** Bindings for RTT module. */ +public class RttBindings { + + public static RttCallScreen createRttCallScreen(String callId) { + return RttChatFragment.newInstance(callId); + } +} diff --git a/java/com/android/incallui/rtt/impl/AndroidManifest.xml b/java/com/android/incallui/rtt/impl/AndroidManifest.xml index fc0705d7e..7f58f71e5 100644 --- a/java/com/android/incallui/rtt/impl/AndroidManifest.xml +++ b/java/com/android/incallui/rtt/impl/AndroidManifest.xml @@ -13,14 +13,5 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<manifest - package="com.android.incallui.rtt.impl" - xmlns:android="http://schemas.android.com/apk/res/android"> - <application android:theme="@style/Theme.AppCompat"> - <activity - android:name=".RttChatActivity" - android:exported="false" - android:theme="@style/DialerThemeBase.NoActionBar" - android:windowSoftInputMode="adjustResize"/> - </application> +<manifest package="com.android.incallui.rtt.impl"> </manifest> diff --git a/java/com/android/incallui/rtt/impl/RttChatActivity.java b/java/com/android/incallui/rtt/impl/RttChatActivity.java deleted file mode 100644 index 96056f746..000000000 --- a/java/com/android/incallui/rtt/impl/RttChatActivity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.impl; - -import android.os.Bundle; -import android.os.SystemClock; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; -import android.view.View; - -/** Activity to for RTT chat window. */ -public class RttChatActivity extends FragmentActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_rtt); - getSupportFragmentManager() - .beginTransaction() - .add( - R.id.fragment_rtt, - RttChatFragment.newInstance("", "Jane Williamson", SystemClock.elapsedRealtime())) - .commit(); - getWindow().setStatusBarColor(getColor(R.color.rtt_status_bar_color)); - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - } -} diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java index 1db4c6bad..1ea7f31b1 100644 --- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java +++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java @@ -17,19 +17,15 @@ package com.android.incallui.rtt.impl; import android.content.Context; -import android.support.annotation.MainThread; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; -import com.android.dialer.common.concurrent.ThreadUtil; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Random; /** Adapter class for holding RTT chat data. */ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolder> { @@ -42,13 +38,11 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde private final List<RttChatMessage> rttMessages = new ArrayList<>(); private int lastIndexOfLocalMessage = -1; private int lastIndexOfRemoteMessage = -1; - private final TypeBot typeBot; private final MessageListener messageListener; RttChatAdapter(Context context, MessageListener listener) { this.context = context; this.messageListener = listener; - typeBot = new TypeBot(text -> ThreadUtil.postOnUiThread(() -> addRemoteMessage(text))); } @Override @@ -133,7 +127,6 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde rttMessages.get(lastIndexOfLocalMessage).finish(); notifyItemChanged(lastIndexOfLocalMessage); lastIndexOfLocalMessage = -1; - startChatBot(); } void addRemoteMessage(String message) { @@ -146,73 +139,4 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde messageListener.newMessageAdded(); } } - - private void startChatBot() { - typeBot.scheduleMessage(); - } - - // TODO(wangqi): Move this out of this class once a bug is fixed. - private static class TypeBot { - interface Callback { - void type(String text); - } - - private static final String[] CANDIDATE_MESSAGES = - new String[] { - "To RTT or not to RTT, that is the question...", - "Making TTY great again!", - "I would be more comfortable with real \"Thyme\" chatting." - + " I don't know how to end this pun", - "お疲れ様でした", - "The FCC has mandated that I respond... I will do so begrudgingly", - "😂😂😂💯" - }; - private final Random random = new Random(); - private final Callback callback; - private final List<String> messageQueue = new ArrayList<>(); - private int currentTypingPosition = -1; - private String currentTypingMessage = null; - - TypeBot(Callback callback) { - this.callback = callback; - } - - @MainThread - public void scheduleMessage() { - Assert.isMainThread(); - if (random.nextDouble() < 0.5) { - return; - } - - String text = CANDIDATE_MESSAGES[random.nextInt(CANDIDATE_MESSAGES.length)]; - messageQueue.add(text); - typeMessage(); - } - - @MainThread - private void typeMessage() { - Assert.isMainThread(); - if (currentTypingPosition < 0 || currentTypingMessage == null) { - if (messageQueue.size() <= 0) { - return; - } - currentTypingMessage = messageQueue.remove(0); - currentTypingPosition = 0; - } - if (currentTypingPosition < currentTypingMessage.length()) { - int size = random.nextInt(currentTypingMessage.length() - currentTypingPosition + 1); - callback.type( - currentTypingMessage.substring(currentTypingPosition, currentTypingPosition + size)); - currentTypingPosition = currentTypingPosition + size; - // Wait up to 2s between typing. - ThreadUtil.postDelayedOnUiThread(this::typeMessage, 200 * random.nextInt(10)); - } else { - callback.type(RttChatMessage.BUBBLE_BREAKER); - currentTypingPosition = -1; - currentTypingMessage = null; - // Wait 1-11s between two messages. - ThreadUtil.postDelayedOnUiThread(this::typeMessage, 1000 * (1 + random.nextInt(10))); - } - } - } } diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index 0b0ad2a8e..c7ee2ff67 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -16,21 +16,25 @@ package com.android.incallui.rtt.impl; +import android.app.Activity; import android.os.Bundle; import android.os.SystemClock; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.OnScrollListener; +import android.telecom.CallAudioState; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.Window; +import android.view.accessibility.AccessibilityEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Chronometer; @@ -38,15 +42,34 @@ import android.widget.EditText; import android.widget.ImageButton; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import com.android.dialer.common.Assert; +import com.android.dialer.common.FragmentUtils; +import com.android.dialer.common.LogUtil; +import com.android.incallui.call.DialerCall.State; +import com.android.incallui.incall.protocol.InCallButtonUi; +import com.android.incallui.incall.protocol.InCallButtonUiDelegate; +import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory; +import com.android.incallui.incall.protocol.InCallScreen; +import com.android.incallui.incall.protocol.InCallScreenDelegate; +import com.android.incallui.incall.protocol.InCallScreenDelegateFactory; +import com.android.incallui.incall.protocol.PrimaryCallState; +import com.android.incallui.incall.protocol.PrimaryInfo; +import com.android.incallui.incall.protocol.SecondaryInfo; import com.android.incallui.rtt.impl.RttChatAdapter.MessageListener; +import com.android.incallui.rtt.protocol.RttCallScreen; +import com.android.incallui.rtt.protocol.RttCallScreenDelegate; +import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory; /** RTT chat fragment to show chat bubbles. */ public class RttChatFragment extends Fragment - implements OnClickListener, OnEditorActionListener, TextWatcher, MessageListener { + implements OnEditorActionListener, + TextWatcher, + MessageListener, + RttCallScreen, + InCallScreen, + InCallButtonUi { private static final String ARG_CALL_ID = "call_id"; - private static final String ARG_NAME_OR_NUMBER = "name_or_number"; - private static final String ARG_SESSION_START_TIME = "session_start_time"; private RecyclerView recyclerView; private RttChatAdapter adapter; @@ -63,27 +86,59 @@ public class RttChatFragment extends Fragment } } }; + private InCallScreenDelegate inCallScreenDelegate; + private RttCallScreenDelegate rttCallScreenDelegate; + private InCallButtonUiDelegate inCallButtonUiDelegate; + private View endCallButton; + private TextView nameTextView; + private Chronometer chronometer; + private boolean isTimerStarted; /** * Create a new instance of RttChatFragment. * * @param callId call id of the RTT call. - * @param nameOrNumber name or number of the caller to be displayed - * @param sessionStartTimeMillis start time of RTT session in terms of {@link - * SystemClock#elapsedRealtime}. * @return new RttChatFragment */ - public static RttChatFragment newInstance( - String callId, String nameOrNumber, long sessionStartTimeMillis) { + public static RttChatFragment newInstance(String callId) { Bundle bundle = new Bundle(); bundle.putString(ARG_CALL_ID, callId); - bundle.putString(ARG_NAME_OR_NUMBER, nameOrNumber); - bundle.putLong(ARG_SESSION_START_TIME, sessionStartTimeMillis); RttChatFragment instance = new RttChatFragment(); instance.setArguments(bundle); return instance; } + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LogUtil.i("RttChatFragment.onCreate", null); + inCallButtonUiDelegate = + FragmentUtils.getParent(this, InCallButtonUiDelegateFactory.class) + .newInCallButtonUiDelegate(); + if (savedInstanceState != null) { + inCallButtonUiDelegate.onRestoreInstanceState(savedInstanceState); + } + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle bundle) { + super.onViewCreated(view, bundle); + LogUtil.i("RttChatFragment.onViewCreated", null); + + inCallScreenDelegate = + FragmentUtils.getParentUnsafe(this, InCallScreenDelegateFactory.class) + .newInCallScreenDelegate(); + rttCallScreenDelegate = + FragmentUtils.getParentUnsafe(this, RttCallScreenDelegateFactory.class) + .newRttCallScreenDelegate(this); + + rttCallScreenDelegate.initRttCallScreenDelegate(getContext(), this); + + inCallScreenDelegate.onInCallScreenDelegateInit(this); + inCallScreenDelegate.onInCallScreenReady(); + inCallButtonUiDelegate.onInCallButtonUiReady(this); + } + @Nullable @Override public View onCreateView( @@ -101,38 +156,27 @@ public class RttChatFragment extends Fragment recyclerView.setAdapter(adapter); recyclerView.addOnScrollListener(onScrollListener); submitButton = view.findViewById(R.id.rtt_chat_submit_button); - submitButton.setOnClickListener(this); + submitButton.setOnClickListener( + v -> { + adapter.submitLocalMessage(); + isClearingInput = true; + editText.setText(""); + isClearingInput = false; + }); submitButton.setEnabled(false); + endCallButton = view.findViewById(R.id.rtt_end_call_button); + endCallButton.setOnClickListener( + v -> { + LogUtil.i("RttChatFragment.onClick", "end call button clicked"); + inCallButtonUiDelegate.onEndCallClicked(); + }); - String nameOrNumber = null; - Bundle bundle = getArguments(); - if (bundle != null) { - nameOrNumber = bundle.getString(ARG_NAME_OR_NUMBER, getString(R.string.unknown)); - } - TextView nameTextView = view.findViewById(R.id.rtt_name_or_number); - nameTextView.setText(nameOrNumber); - - long sessionStartTime = SystemClock.elapsedRealtime(); - if (bundle != null) { - sessionStartTime = bundle.getLong(ARG_SESSION_START_TIME, sessionStartTime); - } - Chronometer chronometer = view.findViewById(R.id.rtt_timer); - chronometer.setBase(sessionStartTime); - chronometer.start(); + nameTextView = view.findViewById(R.id.rtt_name_or_number); + chronometer = view.findViewById(R.id.rtt_timer); return view; } @Override - public void onClick(View v) { - if (v.getId() == R.id.rtt_chat_submit_button) { - adapter.submitLocalMessage(); - isClearingInput = true; - editText.setText(""); - isClearingInput = false; - } - } - - @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_DONE) { submitButton.performClick(); @@ -166,6 +210,20 @@ public class RttChatFragment extends Fragment recyclerView.smoothScrollToPosition(adapter.getItemCount()); } + @Override + public void onStart() { + LogUtil.enterBlock("RttChatFragment.onStart"); + super.onStart(); + onRttScreenStart(); + } + + @Override + public void onStop() { + LogUtil.enterBlock("RttChatFragment.onStop"); + super.onStop(); + onRttScreenStop(); + } + private void hideKeyboard() { InputMethodManager inputMethodManager = getContext().getSystemService(InputMethodManager.class); if (inputMethodManager.isAcceptingText()) { @@ -173,4 +231,129 @@ public class RttChatFragment extends Fragment getActivity().getCurrentFocus().getWindowToken(), 0); } } + + @Override + public void onRttScreenStart() { + rttCallScreenDelegate.onRttCallScreenUiReady(); + Activity activity = getActivity(); + Window window = getActivity().getWindow(); + window.setStatusBarColor(activity.getColor(R.color.rtt_status_bar_color)); + window.setNavigationBarColor(activity.getColor(R.color.rtt_navigation_bar_color)); + window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR); + } + + @Override + public void onRttScreenStop() { + rttCallScreenDelegate.onRttCallScreenUiUnready(); + } + + @Override + public Fragment getRttCallScreenFragment() { + return this; + } + + @Override + public String getCallId() { + return Assert.isNotNull(getArguments().getString(ARG_CALL_ID)); + } + + @Override + public void setPrimary(@NonNull PrimaryInfo primaryInfo) { + LogUtil.i("RttChatFragment.setPrimary", primaryInfo.toString()); + nameTextView.setText(primaryInfo.name); + } + + @Override + public void setSecondary(@NonNull SecondaryInfo secondaryInfo) {} + + @Override + public void setCallState(@NonNull PrimaryCallState primaryCallState) { + LogUtil.i("RttChatFragment.setCallState", primaryCallState.toString()); + if (!isTimerStarted && primaryCallState.state == State.ACTIVE) { + LogUtil.i( + "RttChatFragment.setCallState", "starting timer with base: %d", chronometer.getBase()); + chronometer.setBase( + primaryCallState.connectTimeMillis + - System.currentTimeMillis() + + SystemClock.elapsedRealtime()); + chronometer.start(); + isTimerStarted = true; + } + } + + @Override + public void setEndCallButtonEnabled(boolean enabled, boolean animate) {} + + @Override + public void showManageConferenceCallButton(boolean visible) {} + + @Override + public boolean isManageConferenceVisible() { + return false; + } + + @Override + public void dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {} + + @Override + public void showNoteSentToast() {} + + @Override + public void updateInCallScreenColors() {} + + @Override + public void onInCallScreenDialpadVisibilityChange(boolean isShowing) {} + + @Override + public int getAnswerAndDialpadContainerResourceId() { + return 0; + } + + @Override + public void showLocationUi(Fragment locationUi) {} + + @Override + public boolean isShowingLocationUi() { + return false; + } + + @Override + public Fragment getInCallScreenFragment() { + return this; + } + + @Override + public void showButton(int buttonId, boolean show) {} + + @Override + public void enableButton(int buttonId, boolean enable) {} + + @Override + public void setEnabled(boolean on) {} + + @Override + public void setHold(boolean on) {} + + @Override + public void setCameraSwitched(boolean isBackFacingCamera) {} + + @Override + public void setVideoPaused(boolean isPaused) {} + + @Override + public void setAudioState(CallAudioState audioState) {} + + @Override + public void updateButtonStates() {} + + @Override + public void updateInCallButtonUiColors(int color) {} + + @Override + public Fragment getInCallButtonUiFragment() { + return this; + } + + @Override + public void showAudioRouteSelector() {} } diff --git a/java/com/android/incallui/rtt/impl/RttChatMessage.java b/java/com/android/incallui/rtt/impl/RttChatMessage.java index 85b045183..b36da77cc 100644 --- a/java/com/android/incallui/rtt/impl/RttChatMessage.java +++ b/java/com/android/incallui/rtt/impl/RttChatMessage.java @@ -19,6 +19,7 @@ package com.android.incallui.rtt.impl; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.TextWatcher; +import com.android.incallui.rtt.protocol.Constants; import com.google.common.base.Splitter; import java.util.ArrayList; import java.util.Iterator; @@ -27,8 +28,7 @@ import java.util.List; /** Message class that holds one RTT chat content. */ final class RttChatMessage { - static final String BUBBLE_BREAKER = "\n\n"; - private static final Splitter SPLITTER = Splitter.on(BUBBLE_BREAKER); + private static final Splitter SPLITTER = Splitter.on(Constants.BUBBLE_BREAKER); boolean isRemote; public boolean hasAvatar; @@ -108,7 +108,7 @@ final class RttChatMessage { firstMessage.isRemote = true; } firstMessage.append(firstMessageContent); - if (splitText.hasNext() || text.endsWith(BUBBLE_BREAKER)) { + if (splitText.hasNext() || text.endsWith(Constants.BUBBLE_BREAKER)) { firstMessage.finish(); } messageList.add(firstMessage); diff --git a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml index 7ba6a09e3..5ba9f4ee8 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml +++ b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml @@ -17,7 +17,8 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/dialer_theme_color"> + android:background="@color/dialer_theme_color" + android:fitsSystemWindows="true"> <include layout="@layout/rtt_banner"/> @@ -52,6 +53,8 @@ android:inputType="textMultiLine|text" android:maxLines="4" android:minHeight="53dp" + android:textColor="#DD000000" + android:textColorHint="#757575" android:textSize="16sp"/> <ImageButton android:id="@+id/rtt_chat_submit_button" diff --git a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml index 4ce94f9fb..f1938056f 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml +++ b/java/com/android/incallui/rtt/impl/res/layout/rtt_banner.xml @@ -17,79 +17,61 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="56dp" - android:background="#FAFAFA" + android:layout_height="?attr/actionBarSize" + android:background="#F305228F" android:elevation="3dp"> <ImageButton - android:id="@+id/rtt_back" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:id="@+id/rtt_end_call_button" + android:layout_width="32dp" + android:layout_height="32dp" android:layout_marginStart="16dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" android:background="@android:color/transparent" - android:contentDescription="@string/content_description_rtt_back_button" - android:src="@drawable/quantum_ic_arrow_back_vd_theme_24" - android:tint="#DF000000"/> + android:contentDescription="@string/incall_content_description_end_call" + android:scaleType="fitXY" + android:src="@drawable/quantum_ic_call_end_vd_theme_24" + android:tint="#FFDF0000"/> <LinearLayout - android:layout_width="wrap_content" + android:layout_width="260dp" android:layout_height="match_parent" android:layout_marginTop="8dp" android:layout_marginBottom="8dp" android:layout_marginStart="32dp" - android:layout_toEndOf="@id/rtt_back" + android:layout_toEndOf="@id/rtt_end_call_button" android:orientation="vertical"> <TextView android:id="@+id/rtt_name_or_number" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" android:fontFamily="sans-serif-medium" android:includeFontPadding="false" - android:textColor="#DD000000" + android:textColor="#FFFFFF" android:textSize="20sp" tools:text="Bruce Graham"/> <Chronometer android:id="@+id/rtt_timer" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" android:fontFamily="sans-serif-medium" android:includeFontPadding="false" - android:textColor="#DD000000" + android:textColor="#FFFFFF" android:textSize="14sp" tools:text="00:09"/> </LinearLayout> <ImageButton - android:id="@+id/rtt_hang_up_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="16dp" + android:id="@+id/rtt_overflow_button" + android:layout_width="32dp" + android:layout_height="32dp" + android:layout_marginEnd="12dp" android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:background="@android:color/transparent" - android:contentDescription="@string/incall_content_description_end_call" - android:src="@drawable/quantum_ic_call_end_vd_theme_24" - android:tint="#FFDF0000"/> - <ImageButton - android:id="@+id/rtt_speaker_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_centerVertical="true" - android:layout_toStartOf="@id/rtt_hang_up_button" - android:background="@android:color/transparent" - android:contentDescription="@string/incall_content_description_speaker" - android:src="@drawable/quantum_ic_volume_up_vd_theme_24" - android:tint="#DD000000"/> - <ImageButton - android:id="@+id/rtt_mic_button" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_centerVertical="true" - android:layout_toStartOf="@id/rtt_speaker_button" - android:background="@android:color/transparent" - android:contentDescription="@string/incall_content_description_unmuted" - android:src="@drawable/quantum_ic_mic_off_vd_theme_24" - android:tint="#DD000000"/> + android:contentDescription="@string/content_description_overflow" + android:scaleType="fitXY" + android:src="@drawable/quantum_ic_more_vert_vd_theme_24" + android:tint="#FFFFFF"/> </RelativeLayout>
\ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/colors.xml b/java/com/android/incallui/rtt/impl/res/values/colors.xml index 402cac4a0..c25ad21f2 100644 --- a/java/com/android/incallui/rtt/impl/res/values/colors.xml +++ b/java/com/android/incallui/rtt/impl/res/values/colors.xml @@ -15,5 +15,6 @@ ~ limitations under the License --> <resources> - <color name="rtt_status_bar_color">#E0E0E0</color> + <color name="rtt_status_bar_color">#03165C</color> + <color name="rtt_navigation_bar_color">#FAFAFA</color> </resources>
\ No newline at end of file diff --git a/java/com/android/incallui/rtt/impl/res/values/strings.xml b/java/com/android/incallui/rtt/impl/res/values/strings.xml index 523abdcbc..0b9eb71f4 100644 --- a/java/com/android/incallui/rtt/impl/res/values/strings.xml +++ b/java/com/android/incallui/rtt/impl/res/values/strings.xml @@ -18,9 +18,6 @@ <!-- Content description for submit chat input button. [CHAR LIMIT=NONE] --> <string name="content_description_rtt_check_button">Go ahead</string> - <!-- Content description for navigate back button on RTT chat window. [CHAR LIMIT=NONE] --> - <string name="content_description_rtt_back_button">Back</string> - <!-- Hint text for input box. [CHAR LIMIT=NONE] --> <string name="rtt_input_hint">Type a message</string> diff --git a/java/com/android/incallui/rtt/impl/res/layout/activity_rtt.xml b/java/com/android/incallui/rtt/protocol/AndroidManifest.xml index b48e8d43f..52514a501 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/activity_rtt.xml +++ b/java/com/android/incallui/rtt/protocol/AndroidManifest.xml @@ -1,4 +1,3 @@ -<?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2018 The Android Open Source Project ~ @@ -14,13 +13,10 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <FrameLayout - android:id="@+id/fragment_rtt" - android:layout_width="match_parent" - android:layout_height="match_parent"/> -</LinearLayout>
\ No newline at end of file +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.incallui.rtt.protocol"> + <uses-sdk + android:minSdkVersion="23" + android:targetSdkVersion="26"/> +</manifest>
\ No newline at end of file diff --git a/java/com/android/incallui/rtt/protocol/Constants.java b/java/com/android/incallui/rtt/protocol/Constants.java new file mode 100644 index 000000000..5806bbada --- /dev/null +++ b/java/com/android/incallui/rtt/protocol/Constants.java @@ -0,0 +1,24 @@ +/* + * 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; + +/** Constants for RTT call. */ +public interface Constants { + + /** String used to break bubble, which means one RTT message is complete. */ + String BUBBLE_BREAKER = "\n\n"; +} diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreen.java b/java/com/android/incallui/rtt/protocol/RttCallScreen.java new file mode 100644 index 000000000..afacbae48 --- /dev/null +++ b/java/com/android/incallui/rtt/protocol/RttCallScreen.java @@ -0,0 +1,31 @@ +/* + * 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.v4.app.Fragment; + +/** Interface for call RTT call module. */ +public interface RttCallScreen { + + void onRttScreenStart(); + + void onRttScreenStop(); + + Fragment getRttCallScreenFragment(); + + String getCallId(); +} diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java new file mode 100644 index 000000000..e29c43d70 --- /dev/null +++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java @@ -0,0 +1,29 @@ +/* + * 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.content.Context; + +/** Callbacks from the module out to the container. */ +public interface RttCallScreenDelegate { + + void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen); + + void onRttCallScreenUiReady(); + + void onRttCallScreenUiUnready(); +} diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegateFactory.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegateFactory.java new file mode 100644 index 000000000..0dbcc9135 --- /dev/null +++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegateFactory.java @@ -0,0 +1,23 @@ +/* + * 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; + +/** Callbacks from the module out to the container. */ +public interface RttCallScreenDelegateFactory { + + RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen rttCallScreen); +} |