summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assets/quantum/res/drawable/quantum_ic_report_vd_red_24.xml36
-rw-r--r--java/com/android/contacts/common/model/ContactLoader.java2
-rw-r--r--java/com/android/dialer/app/DialtactsActivity.java8
-rw-r--r--java/com/android/dialer/app/calllog/CallLogFragment.java25
-rw-r--r--java/com/android/dialer/app/res/values/strings.xml4
-rw-r--r--java/com/android/dialer/binary/common/DialerApplication.java7
-rw-r--r--java/com/android/dialer/buildtype/BuildType.java20
-rw-r--r--java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java3
-rw-r--r--java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java92
-rw-r--r--java/com/android/dialer/calllog/CallLogComponent.java5
-rw-r--r--java/com/android/dialer/calllog/CallLogFramework.java70
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java121
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java5
-rw-r--r--java/com/android/dialer/calllog/constants/IntentNames.java30
-rw-r--r--java/com/android/dialer/calllog/constants/SharedPrefKeys.java25
-rw-r--r--java/com/android/dialer/calllog/datasources/CallLogDataSource.java11
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java18
-rw-r--r--java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java57
-rw-r--r--java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java99
-rw-r--r--java/com/android/dialer/calllog/observer/MarkDirtyObserver.java (renamed from java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java)30
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java159
-rw-r--r--java/com/android/dialer/calllog/ui/menu/Modules.java33
-rw-r--r--java/com/android/dialer/commandline/CommandLineReceiver.java3
-rw-r--r--java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java21
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/row_deviceid.xml1
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc310-mnc000/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc310-mnc120/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc311-mnc480/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc311-mnc490/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc311-mnc870/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc312-mnc530/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values-mcc316-mnc010/bools.xml19
-rw-r--r--java/com/android/dialer/dialpadview/res/values/bools.xml20
-rw-r--r--java/com/android/dialer/glidephotomanager/PhotoInfo.java10
-rw-r--r--java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java55
-rw-r--r--java/com/android/dialer/logging/LoggingBindings.java18
-rw-r--r--java/com/android/dialer/logging/LoggingBindingsStub.java9
-rw-r--r--java/com/android/dialer/main/MainActivityPeer.java2
-rw-r--r--java/com/android/dialer/main/impl/AndroidManifest.xml2
-rw-r--r--java/com/android/dialer/main/impl/MainActivity.java7
-rw-r--r--java/com/android/dialer/main/impl/MainSearchController.java65
-rw-r--r--java/com/android/dialer/main/impl/NewMainActivityPeer.java3
-rw-r--r--java/com/android/dialer/main/impl/OldMainActivityPeer.java206
-rw-r--r--java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java4
-rw-r--r--java/com/android/dialer/main/impl/res/layout/main_activity.xml4
-rw-r--r--java/com/android/dialer/main/impl/res/values-v27/styles.xml (renamed from java/com/android/dialer/glidephotomanager/impl/res/drawable/ic_report_red_48dp.xml)28
-rw-r--r--java/com/android/dialer/main/impl/res/values/styles.xml23
-rw-r--r--java/com/android/dialer/main/impl/toolbar/MainToolbar.java46
-rw-r--r--java/com/android/dialer/main/impl/toolbar/SearchBarListener.java7
-rw-r--r--java/com/android/dialer/main/impl/toolbar/SearchBarView.java16
-rw-r--r--java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml2
-rw-r--r--java/com/android/dialer/main/impl/toolbar/res/layout/toolbar_layout.xml9
-rw-r--r--java/com/android/dialer/main/impl/toolbar/res/values/dimens.xml4
-rw-r--r--java/com/android/dialer/metrics/Metrics.java21
-rw-r--r--java/com/android/dialer/metrics/StubMetrics.java9
-rw-r--r--java/com/android/dialer/metrics/jank/RecyclerViewJankLogger.java46
-rw-r--r--java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java111
-rw-r--r--java/com/android/dialer/oem/MotorolaUtils.java3
-rw-r--r--java/com/android/dialer/oem/res/values-mcc310-mnc120/motorola_config.xml56
-rw-r--r--java/com/android/dialer/oem/res/values/motorola_config.xml45
-rw-r--r--java/com/android/dialer/phonelookup/PhoneLookup.java11
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java11
-rw-r--r--java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java11
-rw-r--r--java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java5
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java3
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java3
-rw-r--r--java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java6
-rw-r--r--java/com/android/dialer/phonenumbercache/ContactInfoHelper.java7
-rw-r--r--java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java7
-rw-r--r--java/com/android/dialer/searchfragment/directories/DirectoryContactViewHolder.java (renamed from java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java)12
-rw-r--r--java/com/android/dialer/searchfragment/directories/DirectoryContactsCursor.java (renamed from java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java)17
-rw-r--r--java/com/android/dialer/searchfragment/directories/DirectoryContactsCursorLoader.java (renamed from java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java)14
-rw-r--r--java/com/android/dialer/searchfragment/directories/res/values/strings.xml (renamed from java/com/android/dialer/searchfragment/remote/res/values/strings.xml)0
-rw-r--r--java/com/android/dialer/searchfragment/list/NewSearchFragment.java71
-rw-r--r--java/com/android/dialer/searchfragment/list/SearchAdapter.java12
-rw-r--r--java/com/android/dialer/searchfragment/remote/AndroidManifest.xml16
-rw-r--r--java/com/android/dialer/simulator/impl/RttChatBot.java3
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorConnection.java29
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorImpl.java3
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorRttCall.java12
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java2
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java14
-rw-r--r--java/com/android/dialer/strictmode/StrictModeUtils.java3
-rw-r--r--java/com/android/dialer/theme/res/values/dimens.xml2
-rw-r--r--java/com/android/dialer/theme/res/values/styles.xml2
-rw-r--r--java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java157
-rw-r--r--java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java23
-rw-r--r--java/com/android/dialer/voicemail/settings/res/values/strings.xml16
-rw-r--r--java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml4
-rw-r--r--java/com/android/dialer/widget/TextViewPreference.java141
-rw-r--r--java/com/android/dialer/widget/res/layout/text_view_preference.xml24
-rw-r--r--java/com/android/incallui/CallCardPresenter.java86
-rw-r--r--java/com/android/incallui/InCallActivity.java21
-rw-r--r--java/com/android/incallui/RttCallPresenter.java131
-rw-r--r--java/com/android/incallui/answer/impl/AnswerFragment.java16
-rw-r--r--java/com/android/incallui/call/CallList.java13
-rw-r--r--java/com/android/incallui/call/DialerCall.java11
-rw-r--r--java/com/android/incallui/callpending/CallPendingActivity.java38
-rw-r--r--java/com/android/incallui/contactgrid/BottomRow.java16
-rw-r--r--java/com/android/incallui/contactgrid/ContactGridManager.java28
-rw-r--r--java/com/android/incallui/contactgrid/TopRow.java12
-rw-r--r--java/com/android/incallui/incall/impl/InCallFragment.java4
-rw-r--r--java/com/android/incallui/incall/protocol/PrimaryInfo.java197
-rw-r--r--java/com/android/incallui/rtt/impl/RttChatAdapter.java12
-rw-r--r--java/com/android/incallui/rtt/impl/RttChatFragment.java23
-rw-r--r--java/com/android/incallui/rtt/protocol/RttCallScreen.java2
-rw-r--r--java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java6
-rw-r--r--java/com/android/voicemail/impl/res/xml/vvm_config.xml15
108 files changed, 2211 insertions, 910 deletions
diff --git a/assets/quantum/res/drawable/quantum_ic_report_vd_red_24.xml b/assets/quantum/res/drawable/quantum_ic_report_vd_red_24.xml
new file mode 100644
index 000000000..a64db13c7
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_report_vd_red_24.xml
@@ -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
+ -->
+
+<!-- A custom-made "report" icon for Dialer -->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="40.0"
+ android:viewportWidth="40.0">
+ <path
+ android:fillColor="#D50000"
+ android:fillType="nonZero"
+ android:pathData="M27.87,1L39,12.13L39,27.87L27.87,39L12.13,39L1,27.87L1,12.13L12.13,1L27.87,1Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="evenOdd"
+ android:pathData="M20,30.6C18.56,30.6 17.4,29.44 17.4,28C17.4,26.56 18.56,25.4 20,25.4C21.44,25.4 22.6,26.56 22.6,28C22.6,29.44 21.44,30.6 20,30.6ZM22,22L18,22L18,10L22,10L22,22Z"
+ android:strokeColor="#00000000"
+ android:strokeWidth="1"/>
+</vector>
diff --git a/java/com/android/contacts/common/model/ContactLoader.java b/java/com/android/contacts/common/model/ContactLoader.java
index d2c757709..51b8e3efc 100644
--- a/java/com/android/contacts/common/model/ContactLoader.java
+++ b/java/com/android/contacts/common/model/ContactLoader.java
@@ -218,7 +218,7 @@ public class ContactLoader extends AsyncTaskLoader<Contact> {
@Override
public Contact loadInBackground() {
- LogUtil.e(TAG, "loadInBackground=" + mLookupUri);
+ LogUtil.v(TAG, "loadInBackground=" + mLookupUri);
try {
final ContentResolver resolver = getContext().getContentResolver();
final Uri uriCurrentFormat = ContactLoaderUtils.ensureIsContactUri(resolver, mLookupUri);
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
index 293ebed87..ff64ba168 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -116,9 +116,10 @@ import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCo
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.InteractionEvent;
import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.LoggingBindings;
import com.android.dialer.logging.ScreenEvent;
import com.android.dialer.logging.UiAction;
+import com.android.dialer.metrics.Metrics;
+import com.android.dialer.metrics.MetricsComponent;
import com.android.dialer.p13n.inference.P13nRanking;
import com.android.dialer.p13n.inference.protocol.P13nRanker;
import com.android.dialer.p13n.inference.protocol.P13nRanker.P13nRefreshCompleteListener;
@@ -612,8 +613,9 @@ public class DialtactsActivity extends TransactionSafeActivity
// add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
ThreadUtil.postDelayedOnUiThread(
() ->
- Logger.get(this)
- .logRecordMemory(LoggingBindings.ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
+ MetricsComponent.get(this)
+ .metrics()
+ .recordMemory(Metrics.DIALTACTS_ON_RESUME_MEMORY_EVENT_NAME),
1000);
}
diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java
index 7f635dbca..dd1d98c83 100644
--- a/java/com/android/dialer/app/calllog/CallLogFragment.java
+++ b/java/com/android/dialer/app/calllog/CallLogFragment.java
@@ -58,11 +58,15 @@ import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
import com.android.dialer.common.Assert;
import com.android.dialer.common.FragmentUtils;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.database.CallLogQueryHandler;
import com.android.dialer.database.CallLogQueryHandler.Listener;
import com.android.dialer.location.GeoUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
+import com.android.dialer.metrics.Metrics;
+import com.android.dialer.metrics.MetricsComponent;
+import com.android.dialer.metrics.jank.RecyclerViewJankLogger;
import com.android.dialer.oem.CequintCallerIdManager;
import com.android.dialer.performancereport.PerformanceReport;
import com.android.dialer.phonenumbercache.ContactInfoHelper;
@@ -305,7 +309,13 @@ public class CallLogFragment extends Fragment
protected void setupView(View view) {
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
+ if (ConfigProviderBindings.get(getContext()).getBoolean("is_call_log_item_anim_null", false)) {
+ recyclerView.setItemAnimator(null);
+ }
recyclerView.setHasFixedSize(true);
+ recyclerView.addOnScrollListener(
+ new RecyclerViewJankLogger(
+ MetricsComponent.get(getContext()).metrics(), Metrics.OLD_CALL_LOG_JANK_EVENT_NAME));
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
PerformanceReport.logOnScrollStateChange(recyclerView);
@@ -661,8 +671,19 @@ public class CallLogFragment extends Fragment
multiSelectUnSelectAllViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
multiSelectUnSelectAllViewContent.setAlpha(show ? 0 : 1);
multiSelectUnSelectAllViewContent.animate().alpha(show ? 1 : 0).start();
- FragmentUtils.getParentUnsafe(this, CallLogFragmentListener.class)
- .showMultiSelectRemoveView(show);
+ if (show) {
+ FragmentUtils.getParentUnsafe(this, CallLogFragmentListener.class)
+ .showMultiSelectRemoveView(true);
+ } else {
+ // This method is called after onDestroy. In DialtactsActivity, ListsFragment implements this
+ // interface and never goes away with configuration changes so this is safe. MainActivity
+ // removes that extra layer though, so we need to check if the parent is still there.
+ CallLogFragmentListener listener =
+ FragmentUtils.getParent(this, CallLogFragmentListener.class);
+ if (listener != null) {
+ listener.showMultiSelectRemoveView(false);
+ }
+ }
}
@Override
diff --git a/java/com/android/dialer/app/res/values/strings.xml b/java/com/android/dialer/app/res/values/strings.xml
index 2d5542bbf..34ec611c3 100644
--- a/java/com/android/dialer/app/res/values/strings.xml
+++ b/java/com/android/dialer/app/res/values/strings.xml
@@ -686,8 +686,8 @@
<!-- Content of voicemail donation promo dialog
[CHAR LIMIT=NONE] -->
<string name="voicemail_donation_promo_content">
- Let Google review your voicemail messages to improve transcription quality. Your
- voicemail messages will not be tied to your Google Account.
+ Let Google review your voicemail messages to improve transcription quality.
+ For voicemail transcription analysis, your voicemail messages are stored anonymously.
</string>
<!-- Text for a 'learn more' link at the end of the voicemail donation promo dialog content -->
diff --git a/java/com/android/dialer/binary/common/DialerApplication.java b/java/com/android/dialer/binary/common/DialerApplication.java
index c0e6ae660..c23926021 100644
--- a/java/com/android/dialer/binary/common/DialerApplication.java
+++ b/java/com/android/dialer/binary/common/DialerApplication.java
@@ -23,11 +23,14 @@ import android.support.v4.os.BuildCompat;
import com.android.dialer.blocking.BlockedNumbersAutoMigrator;
import com.android.dialer.blocking.FilteredNumberAsyncQueryHandler;
import com.android.dialer.calllog.CallLogComponent;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.inject.HasRootComponent;
import com.android.dialer.notification.NotificationChannelManager;
import com.android.dialer.persistentlog.PersistentLogger;
import com.android.dialer.strictmode.StrictModeComponent;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
/** A common application subclass for all Dialer build variants. */
public abstract class DialerApplication extends Application implements HasRootComponent {
@@ -46,6 +49,10 @@ public abstract class DialerApplication extends Application implements HasRootCo
DialerExecutorComponent.get(this).dialerExecutorFactory())
.asyncAutoMigrate();
CallLogComponent.get(this).callLogFramework().registerContentObservers(getApplicationContext());
+ Futures.addCallback(
+ CallLogComponent.get(this).getAnnotatedCallLogMigrator().migrate(),
+ new DefaultFutureCallback<>(),
+ MoreExecutors.directExecutor());
PersistentLogger.initialize(this);
if (BuildCompat.isAtLeastO()) {
diff --git a/java/com/android/dialer/buildtype/BuildType.java b/java/com/android/dialer/buildtype/BuildType.java
index 6b6bc2906..c5c41d247 100644
--- a/java/com/android/dialer/buildtype/BuildType.java
+++ b/java/com/android/dialer/buildtype/BuildType.java
@@ -28,15 +28,19 @@ public class BuildType {
/** The type of build. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- BUGFOOD, FISHFOOD, DOGFOOD, RELEASE, TEST,
+ Type.BUGFOOD,
+ Type.FISHFOOD,
+ Type.DOGFOOD,
+ Type.RELEASE,
+ Type.TEST,
})
- public @interface Type {}
-
- public static final int BUGFOOD = 1;
- public static final int FISHFOOD = 2;
- public static final int DOGFOOD = 3;
- public static final int RELEASE = 4;
- public static final int TEST = 5;
+ public @interface Type {
+ int BUGFOOD = 1;
+ int FISHFOOD = 2;
+ int DOGFOOD = 3;
+ int RELEASE = 4;
+ int TEST = 5;
+ }
private static int cachedBuildType;
private static boolean didInitializeBuildType;
diff --git a/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java b/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java
index 70b9f9e37..4019dd011 100644
--- a/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java
+++ b/java/com/android/dialer/buildtype/release/BuildTypeAccessorImpl.java
@@ -16,6 +16,7 @@
package com.android.dialer.buildtype;
+import com.android.dialer.buildtype.BuildType.Type;
import com.android.dialer.proguard.UsedByReflection;
/** Gets the build type. */
@@ -25,6 +26,6 @@ public class BuildTypeAccessorImpl implements BuildTypeAccessor {
@Override
@BuildType.Type
public int getBuildType() {
- return BuildType.RELEASE;
+ return Type.RELEASE;
}
}
diff --git a/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
new file mode 100644
index 000000000..f8c6fcef1
--- /dev/null
+++ b/java/com/android/dialer/calllog/AnnotatedCallLogMigrator.java
@@ -0,0 +1,92 @@
+/*
+ * 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.calllog;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.configprovider.ConfigProviderBindings;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.storage.Unencrypted;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import javax.inject.Inject;
+
+/**
+ * Builds the annotated call log on application create once after the feature is enabled to reduce
+ * the latency the first time call log is shown.
+ */
+public final class AnnotatedCallLogMigrator {
+
+ private static final String PREF_MIGRATED = "annotatedCallLogMigratorMigrated";
+
+ private final Context appContext;
+ private final SharedPreferences sharedPreferences;
+ private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
+ private final ListeningExecutorService backgorundExecutor;
+
+ @Inject
+ AnnotatedCallLogMigrator(
+ @ApplicationContext Context appContext,
+ @Unencrypted SharedPreferences sharedPreferences,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutor,
+ RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker) {
+ this.appContext = appContext;
+ this.sharedPreferences = sharedPreferences;
+ this.backgorundExecutor = backgroundExecutor;
+ this.refreshAnnotatedCallLogWorker = refreshAnnotatedCallLogWorker;
+ }
+
+ /**
+ * Builds the annotated call log on application create once after the feature is enabled to reduce
+ * the latency the first time call log is shown.
+ */
+ public ListenableFuture<Void> migrate() {
+
+ return Futures.transformAsync(
+ shouldMigrate(),
+ (shouldMigrate) -> {
+ if (!shouldMigrate) {
+ return Futures.immediateFuture(null);
+ }
+ return Futures.transform(
+ refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck(),
+ (unused) -> {
+ sharedPreferences.edit().putBoolean(PREF_MIGRATED, true).apply();
+ return null;
+ },
+ MoreExecutors.directExecutor());
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ private ListenableFuture<Boolean> shouldMigrate() {
+ return backgorundExecutor.submit(
+ () -> {
+ if (!(ConfigProviderBindings.get(appContext)
+ .getBoolean("is_nui_shortcut_enabled", false))) {
+ return false;
+ }
+ if (sharedPreferences.getBoolean(PREF_MIGRATED, false)) {
+ return false;
+ }
+ return true;
+ });
+ }
+}
diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java
index bb5bfee2a..a2a5084fc 100644
--- a/java/com/android/dialer/calllog/CallLogComponent.java
+++ b/java/com/android/dialer/calllog/CallLogComponent.java
@@ -16,6 +16,7 @@
package com.android.dialer.calllog;
import android.content.Context;
+import com.android.dialer.calllog.notifier.RefreshAnnotatedCallLogNotifier;
import com.android.dialer.inject.HasRootComponent;
import dagger.Subcomponent;
@@ -25,8 +26,12 @@ public abstract class CallLogComponent {
public abstract CallLogFramework callLogFramework();
+ public abstract RefreshAnnotatedCallLogNotifier getRefreshAnnotatedCallLogNotifier();
+
public abstract RefreshAnnotatedCallLogWorker getRefreshAnnotatedCallLogWorker();
+ public abstract AnnotatedCallLogMigrator getAnnotatedCallLogMigrator();
+
public abstract ClearMissedCalls getClearMissedCalls();
public static CallLogComponent get(Context context) {
diff --git a/java/com/android/dialer/calllog/CallLogFramework.java b/java/com/android/dialer/calllog/CallLogFramework.java
index 440055de6..7da8d9c0c 100644
--- a/java/com/android/dialer/calllog/CallLogFramework.java
+++ b/java/com/android/dialer/calllog/CallLogFramework.java
@@ -17,40 +17,26 @@
package com.android.dialer.calllog;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.support.annotation.MainThread;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.DataSources;
-import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.configprovider.ConfigProviderBindings;
-import com.android.dialer.storage.Unencrypted;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
- * Coordinates work across CallLog data sources to detect if the annotated call log is out of date
- * ("dirty") and update it if necessary.
+ * Coordinates work across {@link DataSources}.
*
* <p>All methods should be called on the main thread.
*/
@Singleton
-public final class CallLogFramework implements CallLogDataSource.ContentObserverCallbacks {
-
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- public static final String PREF_FORCE_REBUILD = "callLogFrameworkForceRebuild";
+public final class CallLogFramework {
private final DataSources dataSources;
- private final SharedPreferences sharedPreferences;
-
- @Nullable private CallLogUi ui;
@Inject
- CallLogFramework(DataSources dataSources, @Unencrypted SharedPreferences sharedPreferences) {
+ CallLogFramework(DataSources dataSources) {
this.dataSources = dataSources;
- this.sharedPreferences = sharedPreferences;
}
/** Registers the content observers for all data sources. */
@@ -63,58 +49,10 @@ public final class CallLogFramework implements CallLogDataSource.ContentObserver
// TODO(zachh): Find a way to access Main#isNewUiEnabled without creating a circular dependency.
if (ConfigProviderBindings.get(appContext).getBoolean("is_nui_shortcut_enabled", false)) {
for (CallLogDataSource dataSource : dataSources.getDataSourcesIncludingSystemCallLog()) {
- dataSource.registerContentObservers(appContext, this);
+ dataSource.registerContentObservers(appContext);
}
} else {
LogUtil.i("CallLogFramework.registerContentObservers", "not registering content observers");
}
}
-
- /**
- * Attach a UI component to the framework so that it may be notified of changes to the annotated
- * call log.
- */
- public void attachUi(CallLogUi ui) {
- LogUtil.enterBlock("CallLogFramework.attachUi");
- this.ui = ui;
- }
-
- /**
- * Detaches the UI from the framework. This should be called when the UI is hidden or destroyed
- * and no longer needs to be notified of changes to the annotated call log.
- */
- public void detachUi() {
- LogUtil.enterBlock("CallLogFramework.detachUi");
- this.ui = null;
- }
-
- /**
- * Marks the call log as dirty and notifies any attached UI components. If there are no UI
- * components currently attached, this is an efficient operation since it is just writing a shared
- * pref.
- *
- * <p>We don't want to actually force a rebuild when there is no UI running because we don't want
- * to be constantly rebuilding the database when the device is sitting on a desk and receiving a
- * lot of calls, for example.
- */
- @Override
- @MainThread
- public void markDirtyAndNotify(Context appContext) {
- Assert.isMainThread();
- LogUtil.enterBlock("CallLogFramework.markDirtyAndNotify");
-
- sharedPreferences.edit().putBoolean(PREF_FORCE_REBUILD, true).apply();
-
- if (ui != null) {
- ui.invalidateUi();
- }
- }
-
- /** Callbacks invoked on listening UI components. */
- public interface CallLogUi {
-
- /** Notifies the call log UI that the annotated call log is out of date. */
- @MainThread
- void invalidateUi();
- }
}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java
new file mode 100644
index 000000000..e0bfcd8a3
--- /dev/null
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogReceiver.java
@@ -0,0 +1,121 @@
+/*
+ * 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.calllog;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.annotation.Nullable;
+import com.android.dialer.calllog.constants.IntentNames;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * A {@link BroadcastReceiver} that starts/cancels refreshing the annotated call log when notified.
+ */
+public final class RefreshAnnotatedCallLogReceiver extends BroadcastReceiver {
+
+ /**
+ * This is a reasonable time that it might take between related call log writes, that also
+ * shouldn't slow down single-writes too much. For example, when populating the database using the
+ * simulator, using this value results in ~6 refresh cycles (on a release build) to write 120 call
+ * log entries.
+ */
+ private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L;
+
+ private final RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
+
+ @Nullable private Runnable refreshAnnotatedCallLogRunnable;
+
+ /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */
+ public static IntentFilter getIntentFilter() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG);
+ intentFilter.addAction(IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG);
+ return intentFilter;
+ }
+
+ public RefreshAnnotatedCallLogReceiver(Context context) {
+ refreshAnnotatedCallLogWorker =
+ CallLogComponent.get(context).getRefreshAnnotatedCallLogWorker();
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.onReceive");
+
+ String action = intent.getAction();
+
+ if (IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG.equals(action)) {
+ boolean checkDirty = intent.getBooleanExtra(IntentNames.EXTRA_CHECK_DIRTY, false);
+ refreshAnnotatedCallLog(checkDirty);
+ } else if (IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG.equals(action)) {
+ cancelRefreshingAnnotatedCallLog();
+ }
+ }
+
+ /**
+ * Request a refresh of the annotated call log.
+ *
+ * <p>Note that the execution will be delayed by {@link #REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS}.
+ * Once the work begins, it can't be cancelled.
+ *
+ * @see #cancelRefreshingAnnotatedCallLog()
+ */
+ private void refreshAnnotatedCallLog(boolean checkDirty) {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.refreshAnnotatedCallLog");
+
+ // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
+ // in quick succession don't result in too much work. For example, if we get 10 requests in
+ // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
+ // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
+ // are serialized in RefreshAnnotatedCallLogWorker.
+ //
+ // We might get many requests in quick succession, for example, when the simulator inserts
+ // hundreds of rows into the system call log, or when the data for a new call is incrementally
+ // written to different columns as it becomes available.
+ ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+
+ refreshAnnotatedCallLogRunnable =
+ () -> {
+ ListenableFuture<Void> future =
+ checkDirty
+ ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
+ : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
+ Futures.addCallback(
+ future, new DefaultFutureCallback<>(), MoreExecutors.directExecutor());
+ };
+
+ ThreadUtil.getUiThreadHandler()
+ .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS);
+ }
+
+ /**
+ * When a refresh is requested, its execution is delayed (see {@link
+ * #refreshAnnotatedCallLog(boolean)}). This method only cancels the refresh if it hasn't started.
+ */
+ private void cancelRefreshingAnnotatedCallLog() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogReceiver.cancelRefreshingAnnotatedCallLog");
+
+ ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+ }
+}
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index 4c5904ef1..a430d14a8 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -18,6 +18,7 @@ package com.android.dialer.calllog;
import android.content.Context;
import android.content.SharedPreferences;
+import com.android.dialer.calllog.constants.SharedPrefKeys;
import com.android.dialer.calllog.database.CallLogDatabaseComponent;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.CallLogMutations;
@@ -95,7 +96,7 @@ public class RefreshAnnotatedCallLogWorker {
// Default to true. If the pref doesn't exist, the annotated call log hasn't been
// created and we just skip isDirty checks and force a rebuild.
boolean forceRebuildPrefValue =
- sharedPreferences.getBoolean(CallLogFramework.PREF_FORCE_REBUILD, true);
+ sharedPreferences.getBoolean(SharedPrefKeys.FORCE_REBUILD, true);
if (forceRebuildPrefValue) {
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
@@ -183,7 +184,7 @@ public class RefreshAnnotatedCallLogWorker {
return Futures.transform(
onSuccessfulFillFuture,
unused -> {
- sharedPreferences.edit().putBoolean(CallLogFramework.PREF_FORCE_REBUILD, false).apply();
+ sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, false).apply();
return null;
},
backgroundExecutorService);
diff --git a/java/com/android/dialer/calllog/constants/IntentNames.java b/java/com/android/dialer/calllog/constants/IntentNames.java
new file mode 100644
index 000000000..3912450a1
--- /dev/null
+++ b/java/com/android/dialer/calllog/constants/IntentNames.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.calllog.constants;
+
+/** A class containing names for call log intents. */
+public final class IntentNames {
+
+ public static final String ACTION_REFRESH_ANNOTATED_CALL_LOG = "refresh_annotated_call_log";
+
+ public static final String ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG =
+ "cancel_refreshing_annotated_call_log";
+
+ public static final String EXTRA_CHECK_DIRTY = "check_dirty";
+
+ private IntentNames() {}
+}
diff --git a/java/com/android/dialer/calllog/constants/SharedPrefKeys.java b/java/com/android/dialer/calllog/constants/SharedPrefKeys.java
new file mode 100644
index 000000000..41e65bb53
--- /dev/null
+++ b/java/com/android/dialer/calllog/constants/SharedPrefKeys.java
@@ -0,0 +1,25 @@
+/*
+ * 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.calllog.constants;
+
+/** Keys for shared preferences in the call log package. */
+public final class SharedPrefKeys {
+
+ public static final String FORCE_REBUILD = "force_rebuild";
+
+ private SharedPrefKeys() {}
+}
diff --git a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
index 60654a81a..dbed1d81c 100644
--- a/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/CallLogDataSource.java
@@ -104,14 +104,5 @@ public interface CallLogDataSource {
ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc);
@MainThread
- void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks);
-
- /**
- * Methods which may optionally be called as a result of a data source's content observer firing.
- */
- interface ContentObserverCallbacks {
- @MainThread
- void markDirtyAndNotify(Context appContext);
- }
+ void registerContentObservers(Context appContext);
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 8dec43759..40788f42a 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -61,8 +61,7 @@ import javax.inject.Inject;
* Responsible for maintaining the columns in the annotated call log which are derived from phone
* numbers.
*/
-public final class PhoneLookupDataSource
- implements CallLogDataSource, PhoneLookup.ContentObserverCallbacks {
+public final class PhoneLookupDataSource implements CallLogDataSource {
private final PhoneLookup<PhoneLookupInfo> phoneLookup;
private final ListeningExecutorService backgroundExecutorService;
@@ -85,8 +84,6 @@ public final class PhoneLookupDataSource
*/
private final Set<String> phoneLookupHistoryRowsToDelete = new ArraySet<>();
- private CallLogDataSource.ContentObserverCallbacks dataSourceContentObserverCallbacks;
-
@Inject
PhoneLookupDataSource(
PhoneLookup<PhoneLookupInfo> phoneLookup,
@@ -288,17 +285,8 @@ public final class PhoneLookupDataSource
@MainThread
@Override
- public void registerContentObservers(
- Context appContext, CallLogDataSource.ContentObserverCallbacks contentObserverCallbacks) {
- dataSourceContentObserverCallbacks = contentObserverCallbacks;
- phoneLookup.registerContentObservers(appContext, this);
- }
-
- @MainThread
- @Override
- public void markDirtyAndNotify(Context appContext) {
- Assert.isMainThread();
- dataSourceContentObserverCallbacks.markDirtyAndNotify(appContext);
+ public void registerContentObservers(Context appContext) {
+ phoneLookup.registerContentObservers(appContext);
}
private static ImmutableSet<DialerPhoneNumber>
diff --git a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
index ee484d95e..6daa5e757 100644
--- a/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/systemcalllog/SystemCallLogDataSource.java
@@ -20,13 +20,10 @@ import android.Manifest.permission;
import android.annotation.TargetApi;
import android.content.ContentValues;
import android.content.Context;
-import android.database.ContentObserver;
import android.database.Cursor;
-import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
-import android.os.Handler;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.VoicemailContract;
@@ -47,11 +44,11 @@ import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.Ann
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.calllog.observer.MarkDirtyObserver;
import com.android.dialer.calllogutils.PhoneAccountUtils;
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.ThreadUtil;
import com.android.dialer.compat.android.provider.VoicemailCompat;
import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.storage.StorageComponent;
@@ -79,18 +76,21 @@ public class SystemCallLogDataSource implements CallLogDataSource {
static final String PREF_LAST_TIMESTAMP_PROCESSED = "systemCallLogLastTimestampProcessed";
private final ListeningExecutorService backgroundExecutorService;
+ private final MarkDirtyObserver markDirtyObserver;
@Nullable private Long lastTimestampProcessed;
@Inject
- SystemCallLogDataSource(@BackgroundExecutor ListeningExecutorService backgroundExecutorService) {
+ SystemCallLogDataSource(
+ @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
+ MarkDirtyObserver markDirtyObserver) {
this.backgroundExecutorService = backgroundExecutorService;
+ this.markDirtyObserver = markDirtyObserver;
}
@MainThread
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
Assert.isMainThread();
LogUtil.enterBlock("SystemCallLogDataSource.registerContentObservers");
@@ -101,12 +101,13 @@ public class SystemCallLogDataSource implements CallLogDataSource {
}
// TODO(zachh): Need to somehow register observers if user enables permission after launch?
- CallLogObserver callLogObserver =
- new CallLogObserver(ThreadUtil.getUiThreadHandler(), appContext, contentObserverCallbacks);
-
+ // The system call log has a last updated timestamp, but deletes are physical (the "deleted"
+ // column is unused). This means that we can't detect deletes without scanning the entire table,
+ // which would be too slow. So, we just rely on content observers to trigger rebuilds when any
+ // change is made to the system call log.
appContext
.getContentResolver()
- .registerContentObserver(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, true, callLogObserver);
+ .registerContentObserver(CallLog.Calls.CONTENT_URI_WITH_VOICEMAIL, true, markDirtyObserver);
if (!PermissionsUtil.hasAddVoicemailPermissions(appContext)) {
LogUtil.i("SystemCallLogDataSource.registerContentObservers", "no add voicemail permissions");
@@ -115,7 +116,7 @@ public class SystemCallLogDataSource implements CallLogDataSource {
// TODO(uabdullah): Need to somehow register observers if user enables permission after launch?
appContext
.getContentResolver()
- .registerContentObserver(VoicemailContract.Status.CONTENT_URI, true, callLogObserver);
+ .registerContentObserver(VoicemailContract.Status.CONTENT_URI, true, markDirtyObserver);
}
@Override
@@ -523,36 +524,4 @@ public class SystemCallLogDataSource implements CallLogDataSource {
}
return ids;
}
-
- private static class CallLogObserver extends ContentObserver {
- private final Context appContext;
- private final ContentObserverCallbacks contentObserverCallbacks;
-
- CallLogObserver(
- Handler handler, Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
- super(handler);
- this.appContext = appContext;
- this.contentObserverCallbacks = contentObserverCallbacks;
- }
-
- @MainThread
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- Assert.isMainThread();
- LogUtil.i(
- "SystemCallLogDataSource.CallLogObserver.onChange",
- "Uri:%s, SelfChange:%b",
- String.valueOf(uri),
- selfChange);
- super.onChange(selfChange, uri);
-
- /*
- * The system call log has a last updated timestamp, but deletes are physical (the "deleted"
- * column is unused). This means that we can't detect deletes without scanning the entire
- * table, which would be too slow. So, we just rely on content observers to trigger rebuilds
- * when any change is made to the system call log.
- */
- contentObserverCallbacks.markDirtyAndNotify(appContext);
- }
- }
}
diff --git a/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java b/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java
new file mode 100644
index 000000000..5b73ad778
--- /dev/null
+++ b/java/com/android/dialer/calllog/notifier/RefreshAnnotatedCallLogNotifier.java
@@ -0,0 +1,99 @@
+/*
+ * 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.calllog.notifier;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.support.v4.content.LocalBroadcastManager;
+import com.android.dialer.calllog.constants.IntentNames;
+import com.android.dialer.calllog.constants.SharedPrefKeys;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.storage.Unencrypted;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Notifies that a refresh of the annotated call log needs to be started/cancelled.
+ *
+ * <p>Methods in this class are usually invoked when the underlying data backing the annotated call
+ * log change.
+ *
+ * <p>For example, a {@link android.database.ContentObserver} for the system call log can use {@link
+ * #markDirtyAndNotify()} to force the annotated call log to be rebuilt.
+ */
+@Singleton
+public class RefreshAnnotatedCallLogNotifier {
+
+ private final Context appContext;
+ private final SharedPreferences sharedPreferences;
+
+ @Inject
+ RefreshAnnotatedCallLogNotifier(
+ @ApplicationContext Context appContext, @Unencrypted SharedPreferences sharedPreferences) {
+ this.appContext = appContext;
+ this.sharedPreferences = sharedPreferences;
+ }
+
+ /**
+ * Mark the annotated call log as "dirty" and notify that it needs to be refreshed.
+ *
+ * <p>This will force a rebuild by skip checking whether the annotated call log is "dirty".
+ */
+ public void markDirtyAndNotify() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogNotifier.markDirtyAndNotify");
+
+ sharedPreferences.edit().putBoolean(SharedPrefKeys.FORCE_REBUILD, true).apply();
+ notify(/* checkDirty = */ false);
+ }
+
+ /**
+ * Notifies that the annotated call log needs to be refreshed.
+ *
+ * <p>Note that the notification is sent as a broadcast, which means the annotated call log might
+ * not be refreshed if there is no corresponding receiver registered.
+ *
+ * @param checkDirty Whether to check if the annotated call log is "dirty" before proceeding to
+ * rebuild it.
+ */
+ public void notify(boolean checkDirty) {
+ LogUtil.i("RefreshAnnotatedCallLogNotifier.notify", "checkDirty = %s", checkDirty);
+
+ Intent intent = new Intent();
+ intent.setAction(IntentNames.ACTION_REFRESH_ANNOTATED_CALL_LOG);
+ intent.putExtra(IntentNames.EXTRA_CHECK_DIRTY, checkDirty);
+
+ LocalBroadcastManager.getInstance(appContext).sendBroadcast(intent);
+ }
+
+ /**
+ * Notifies to cancel refreshing the annotated call log.
+ *
+ * <p>Note that this method does not guarantee the job to be cancelled. As the notification is
+ * sent as a broadcast, please see the corresponding receiver for details about cancelling the
+ * job.
+ */
+ public void cancel() {
+ LogUtil.enterBlock("RefreshAnnotatedCallLogNotifier.cancel");
+
+ Intent intent = new Intent();
+ intent.setAction(IntentNames.ACTION_CANCEL_REFRESHING_ANNOTATED_CALL_LOG);
+
+ LocalBroadcastManager.getInstance(appContext).sendBroadcast(intent);
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java b/java/com/android/dialer/calllog/observer/MarkDirtyObserver.java
index 1c41d8f7f..8ab1974bc 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/MarkDirtyObserver.java
+++ b/java/com/android/dialer/calllog/observer/MarkDirtyObserver.java
@@ -14,34 +14,40 @@
* limitations under the License
*/
-package com.android.dialer.phonelookup.blockednumber;
+package com.android.dialer.calllog.observer;
-import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.support.annotation.MainThread;
+import android.support.annotation.VisibleForTesting;
+import com.android.dialer.calllog.notifier.RefreshAnnotatedCallLogNotifier;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.phonelookup.PhoneLookup.ContentObserverCallbacks;
+import javax.inject.Inject;
-/** Calls {@link ContentObserverCallbacks#markDirtyAndNotify(Context)} when the content changed */
-class MarkDirtyObserver extends ContentObserver {
+/**
+ * Mark the annotated call log as dirty and notify that a refresh is in order when the content
+ * changes.
+ */
+public final class MarkDirtyObserver extends ContentObserver {
- private final Context appContext;
- private final ContentObserverCallbacks contentObserverCallbacks;
+ private final RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier;
- MarkDirtyObserver(Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+ @Inject
+ public MarkDirtyObserver(RefreshAnnotatedCallLogNotifier refreshAnnotatedCallLogNotifier) {
super(ThreadUtil.getUiThreadHandler());
- this.appContext = appContext;
- this.contentObserverCallbacks = contentObserverCallbacks;
+ this.refreshAnnotatedCallLogNotifier = refreshAnnotatedCallLogNotifier;
}
@MainThread
@Override
public void onChange(boolean selfChange, Uri uri) {
Assert.isMainThread();
- LogUtil.enterBlock("SystemBlockedNumberPhoneLookup.FilteredNumberObserver.onChange");
- contentObserverCallbacks.markDirtyAndNotify(appContext);
+ LogUtil.i(
+ "MarkDirtyObserver.onChange", "Uri:%s, SelfChange:%b", String.valueOf(uri), selfChange);
+
+ refreshAnnotatedCallLogNotifier.markDirtyAndNotify();
}
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 5e676f072..bb1a7303e 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -22,44 +22,32 @@ import android.support.annotation.VisibleForTesting;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
-import com.android.dialer.calllog.CallLogFramework;
-import com.android.dialer.calllog.CallLogFramework.CallLogUi;
-import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker;
+import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DefaultFutureCallback;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.common.concurrent.ThreadUtil;
-import com.android.dialer.common.concurrent.UiListener;
+import com.android.dialer.metrics.Metrics;
+import com.android.dialer.metrics.MetricsComponent;
+import com.android.dialer.metrics.jank.RecyclerViewJankLogger;
import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.TimeUnit;
/** The "new" call log fragment implementation, which is built on top of the annotated call log. */
-public final class NewCallLogFragment extends Fragment
- implements CallLogUi, LoaderCallbacks<Cursor> {
-
- /*
- * This is a reasonable time that it might take between related call log writes, that also
- * shouldn't slow down single-writes too much. For example, when populating the database using
- * the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120
- * call log entries.
- */
- private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L;
+public final class NewCallLogFragment extends Fragment implements LoaderCallbacks<Cursor> {
@VisibleForTesting
static final long MARK_ALL_CALLS_READ_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
- private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
- private UiListener<Void> refreshAnnotatedCallLogListener;
+ private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
private RecyclerView recyclerView;
- @Nullable private Runnable refreshAnnotatedCallLogRunnable;
private boolean shouldMarkCallsRead = false;
private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true;
@@ -74,16 +62,7 @@ public final class NewCallLogFragment extends Fragment
LogUtil.enterBlock("NewCallLogFragment.onActivityCreated");
- CallLogComponent component = CallLogComponent.get(getContext());
- CallLogFramework callLogFramework = component.callLogFramework();
- callLogFramework.attachUi(this);
-
- // TODO(zachh): Use support fragment manager and add support for them in executors library.
- refreshAnnotatedCallLogListener =
- DialerExecutorComponent.get(getContext())
- .createUiListener(
- getActivity().getFragmentManager(), "NewCallLogFragment.refreshAnnotatedCallLog");
- refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker();
+ refreshAnnotatedCallLogReceiver = new RefreshAnnotatedCallLogReceiver(getContext());
}
@Override
@@ -97,13 +76,52 @@ public final class NewCallLogFragment extends Fragment
public void onResume() {
super.onResume();
- LogUtil.enterBlock("NewCallLogFragment.onResume");
+ boolean isHidden = isHidden();
+ LogUtil.i("NewCallLogFragment.onResume", "isHidden = %s", isHidden);
+
+ // As a fragment's onResume() is tied to the containing Activity's onResume(), being resumed is
+ // not equivalent to becoming visible.
+ // For example, when an activity with a hidden fragment is resumed, the fragment's onResume()
+ // will be called but it is not visible.
+ if (!isHidden) {
+ onFragmentShown();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ LogUtil.enterBlock("NewCallLogFragment.onPause");
+
+ onFragmentHidden();
+ }
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.attachUi(this);
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ LogUtil.i("NewCallLogFragment.onHiddenChanged", "hidden = %s", hidden);
- // TODO(zachh): Consider doing this when fragment becomes visible.
- refreshAnnotatedCallLog(true /* checkDirty */);
+ if (hidden) {
+ onFragmentHidden();
+ } else {
+ onFragmentShown();
+ }
+ }
+
+ /**
+ * To be called when the fragment becomes visible.
+ *
+ * <p>Note that for a fragment, being resumed is not equivalent to becoming visible.
+ *
+ * <p>For example, when an activity with a hidden fragment is resumed, the fragment's onResume()
+ * will be called but it is not visible.
+ */
+ private void onFragmentShown() {
+ registerRefreshAnnotatedCallLogReceiver();
+
+ CallLogComponent.get(getContext())
+ .getRefreshAnnotatedCallLogNotifier()
+ .notify(/* checkDirty = */ true);
// There are some types of data that we show in the call log that are not represented in the
// AnnotatedCallLog. For example, CP2 information for invalid numbers can sometimes only be
@@ -123,18 +141,22 @@ public final class NewCallLogFragment extends Fragment
.postDelayed(setShouldMarkCallsReadTrue, MARK_ALL_CALLS_READ_WAIT_MILLIS);
}
- @Override
- public void onPause() {
- super.onPause();
-
- LogUtil.enterBlock("NewCallLogFragment.onPause");
-
+ /**
+ * To be called when the fragment becomes hidden.
+ *
+ * <p>This can happen in the following two cases:
+ *
+ * <ul>
+ * <li>hide the fragment but keep the parent activity visible (e.g., calling {@link
+ * android.support.v4.app.FragmentTransaction#hide(Fragment)} in an activity, or
+ * <li>the parent activity is paused.
+ * </ul>
+ */
+ private void onFragmentHidden() {
// This is pending work that we don't actually need to follow through with.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
ThreadUtil.getUiThreadHandler().removeCallbacks(setShouldMarkCallsReadTrue);
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.detachUi();
+ unregisterRefreshAnnotatedCallLogReceiver();
if (shouldMarkCallsRead) {
Futures.addCallback(
@@ -151,48 +173,31 @@ public final class NewCallLogFragment extends Fragment
View view = inflater.inflate(R.layout.new_call_log_fragment, container, false);
recyclerView = view.findViewById(R.id.new_call_log_recycler_view);
+ recyclerView.addOnScrollListener(
+ new RecyclerViewJankLogger(
+ MetricsComponent.get(getContext()).metrics(), Metrics.NEW_CALL_LOG_JANK_EVENT_NAME));
getLoaderManager().restartLoader(0, null, this);
return view;
}
- private void refreshAnnotatedCallLog(boolean checkDirty) {
- LogUtil.enterBlock("NewCallLogFragment.refreshAnnotatedCallLog");
-
- // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
- // in quick succession don't result in too much work. For example, if we get 10 requests in
- // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
- // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
- // are serialized in RefreshAnnotatedCallLogWorker.
- //
- // We might get many requests in quick succession, for example, when the simulator inserts
- // hundreds of rows into the system call log, or when the data for a new call is incrementally
- // written to different columns as it becomes available.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
-
- refreshAnnotatedCallLogRunnable =
- () -> {
- ListenableFuture<Void> future =
- checkDirty
- ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
- : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
- refreshAnnotatedCallLogListener.listen(
- getContext(),
- future,
- unused -> {},
- throwable -> {
- throw new RuntimeException(throwable);
- });
- };
- ThreadUtil.getUiThreadHandler()
- .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS);
+ private void registerRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewCallLogFragment.registerRefreshAnnotatedCallLogReceiver");
+
+ LocalBroadcastManager.getInstance(getContext())
+ .registerReceiver(
+ refreshAnnotatedCallLogReceiver, RefreshAnnotatedCallLogReceiver.getIntentFilter());
}
- @Override
- public void invalidateUi() {
- LogUtil.enterBlock("NewCallLogFragment.invalidateUi");
- refreshAnnotatedCallLog(false /* checkDirty */);
+ private void unregisterRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewCallLogFragment.unregisterRefreshAnnotatedCallLogReceiver");
+
+ // Cancel pending work as we don't need it any more.
+ CallLogComponent.get(getContext()).getRefreshAnnotatedCallLogNotifier().cancel();
+
+ LocalBroadcastManager.getInstance(getContext())
+ .unregisterReceiver(refreshAnnotatedCallLogReceiver);
}
@Override
diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java
index c85a9fddd..a13f2e333 100644
--- a/java/com/android/dialer/calllog/ui/menu/Modules.java
+++ b/java/com/android/dialer/calllog/ui/menu/Modules.java
@@ -51,7 +51,7 @@ final class Modules {
PhoneNumberHelper.canPlaceCallsTo(normalizedNumber, row.numberPresentation());
if (canPlaceCalls) {
- addModuleForVideoOrAudioCall(context, modules, row, normalizedNumber);
+ addModuleForCalls(context, modules, row, normalizedNumber);
SharedModules.maybeAddModuleForSendingTextMessage(
context, modules, normalizedNumber, row.numberAttributes().getIsBlocked());
}
@@ -90,12 +90,12 @@ final class Modules {
return modules;
}
- private static void addModuleForVideoOrAudioCall(
+ private static void addModuleForCalls(
Context context,
List<ContactActionModule> modules,
CoalescedRow row,
String normalizedNumber) {
- // If a number is blocked, skip this menu item.
+ // Don't add call options if a number is blocked.
if (row.numberAttributes().getIsBlocked()) {
return;
}
@@ -104,28 +104,21 @@ final class Modules {
TelecomUtil.composePhoneAccountHandle(
row.phoneAccountComponentName(), row.phoneAccountId());
- // For a spam number, only audio calls are allowed.
- if (row.numberAttributes().getIsSpam()) {
- modules.add(
- IntentModule.newCallModule(
- context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
- return;
- }
+ // Add an audio call item
+ modules.add(
+ IntentModule.newCallModule(
+ context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
- if ((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
- // Add an audio call item for video calls. Clicking the top entry on the bottom sheet will
- // trigger a video call.
- modules.add(
- IntentModule.newCallModule(
- context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
- } else {
- // Add a video call item for audio calls. Click the top entry on the bottom sheet will
- // trigger an audio call.
- // TODO(zachh): Only show video option if video capabilities present?
+ // Add a video item if (1) the call log entry is for a video call, and (2) the call is not spam.
+ if ((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO
+ && !row.numberAttributes().getIsSpam()) {
modules.add(
IntentModule.newVideoCallModule(
context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
}
+
+ // TODO(zachh): Also show video option if the call log entry is for an audio call but video
+ // capabilities are present?
}
private static void addModuleForAccessingCallDetails(
diff --git a/java/com/android/dialer/commandline/CommandLineReceiver.java b/java/com/android/dialer/commandline/CommandLineReceiver.java
index e5e78c46a..effca2e92 100644
--- a/java/com/android/dialer/commandline/CommandLineReceiver.java
+++ b/java/com/android/dialer/commandline/CommandLineReceiver.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.buildtype.BuildType.Type;
import com.android.dialer.commandline.Command.IllegalCommandLineArgumentException;
import com.android.dialer.common.LogUtil;
import com.google.common.util.concurrent.FutureCallback;
@@ -44,7 +45,7 @@ public class CommandLineReceiver extends BroadcastReceiver {
LogUtil.e("CommandLineReceiver", "missing tag");
return;
}
- if (!LogUtil.isDebugEnabled() && BuildType.get() != BuildType.BUGFOOD) {
+ if (!LogUtil.isDebugEnabled() && BuildType.get() != Type.BUGFOOD) {
LogUtil.i(outputTag, "DISABLED");
return;
}
diff --git a/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java b/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
index c349d2235..3321d93f2 100644
--- a/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
+++ b/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
@@ -341,11 +341,22 @@ public class SpecialCharSequenceMgr {
for (int slot = 0; slot < telephonyManager.getPhoneCount(); slot++) {
String deviceId = telephonyManager.getDeviceId(slot);
if (!TextUtils.isEmpty(deviceId)) {
- addDeviceIdRow(holder, deviceId, /* showBarcode */ false);
+ addDeviceIdRow(
+ holder,
+ deviceId,
+ /* showDecimal */
+ context.getResources().getBoolean(R.bool.show_device_id_in_hex_and_decimal),
+ /* showBarcode */ false);
}
}
} else {
- addDeviceIdRow(holder, telephonyManager.getDeviceId(), /* showBarcode */ true);
+ addDeviceIdRow(
+ holder,
+ telephonyManager.getDeviceId(),
+ /* showDecimal */
+ context.getResources().getBoolean(R.bool.show_device_id_in_hex_and_decimal),
+ /* showBarcode */
+ context.getResources().getBoolean(R.bool.show_device_id_as_barcode));
}
new AlertDialog.Builder(context)
@@ -361,7 +372,8 @@ public class SpecialCharSequenceMgr {
return false;
}
- private static void addDeviceIdRow(ViewGroup holder, String deviceId, boolean showBarcode) {
+ private static void addDeviceIdRow(
+ ViewGroup holder, String deviceId, boolean showDecimal, boolean showBarcode) {
if (TextUtils.isEmpty(deviceId)) {
return;
}
@@ -378,11 +390,12 @@ public class SpecialCharSequenceMgr {
// If this is the valid length IMEI or MEID (14 digits), show it in all formats, otherwise fall
// back to just showing the raw hex
- if (hex.length() == 14) {
+ if (hex.length() == 14 && showDecimal) {
((TextView) row.findViewById(R.id.deviceid_hex)).setText(hex);
((TextView) row.findViewById(R.id.deviceid_dec)).setText(getDecimalFromHex(hex));
row.findViewById(R.id.deviceid_dec_label).setVisibility(View.VISIBLE);
} else {
+ row.findViewById(R.id.deviceid_hex_label).setVisibility(View.GONE);
((TextView) row.findViewById(R.id.deviceid_hex)).setText(deviceId);
}
diff --git a/java/com/android/dialer/dialpadview/res/layout/row_deviceid.xml b/java/com/android/dialer/dialpadview/res/layout/row_deviceid.xml
index 53f3e5d0d..efeeb5f1e 100644
--- a/java/com/android/dialer/dialpadview/res/layout/row_deviceid.xml
+++ b/java/com/android/dialer/dialpadview/res/layout/row_deviceid.xml
@@ -19,6 +19,7 @@
android:layout_marginTop="?dialogPreferredPadding"
android:orientation="vertical">
<TextView
+ android:id="@+id/deviceid_hex_label"
style="@style/DeviceIdBody"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc310-mnc000/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc310-mnc000/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc310-mnc000/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc310-mnc120/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc310-mnc120/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc310-mnc120/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc311-mnc480/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc480/bools.xml
new file mode 100644
index 000000000..7e549e825
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc480/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_in_hex_and_decimal">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc311-mnc490/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc490/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc490/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc311-mnc870/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc870/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc311-mnc870/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc312-mnc530/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc312-mnc530/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc312-mnc530/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values-mcc316-mnc010/bools.xml b/java/com/android/dialer/dialpadview/res/values-mcc316-mnc010/bools.xml
new file mode 100644
index 000000000..99fa7da7f
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values-mcc316-mnc010/bools.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_as_barcode">true</bool>
+</resources>
diff --git a/java/com/android/dialer/dialpadview/res/values/bools.xml b/java/com/android/dialer/dialpadview/res/values/bools.xml
new file mode 100644
index 000000000..b7fed07e7
--- /dev/null
+++ b/java/com/android/dialer/dialpadview/res/values/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<resources>
+ <bool name="show_device_id_in_hex_and_decimal">false</bool>
+ <bool name="show_device_id_as_barcode">false</bool>
+</resources>
diff --git a/java/com/android/dialer/glidephotomanager/PhotoInfo.java b/java/com/android/dialer/glidephotomanager/PhotoInfo.java
index e4dd87152..016221f40 100644
--- a/java/com/android/dialer/glidephotomanager/PhotoInfo.java
+++ b/java/com/android/dialer/glidephotomanager/PhotoInfo.java
@@ -61,13 +61,6 @@ public abstract class PhotoInfo {
*/
public abstract boolean isVideo();
- /**
- * Should the result be circularized.
- *
- * <p>Defaults to true.
- */
- public abstract boolean isCircular();
-
/** Builder for {@link PhotoInfo} */
@AutoValue.Builder
public abstract static class Builder {
@@ -92,8 +85,6 @@ public abstract class PhotoInfo {
public abstract Builder setIsVideo(boolean isVideo);
- public abstract Builder setIsCircular(boolean isCircular);
-
public abstract PhotoInfo build();
}
@@ -104,7 +95,6 @@ public abstract class PhotoInfo {
.setIsVoicemail(false)
.setIsBlocked(false)
.setIsSpam(false)
- .setIsCircular(true)
.setIsVideo(false);
}
}
diff --git a/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java b/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java
index 10c4dfb4c..e14e604a1 100644
--- a/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java
+++ b/java/com/android/dialer/glidephotomanager/impl/GlidePhotoManagerImpl.java
@@ -51,16 +51,7 @@ public class GlidePhotoManagerImpl implements GlidePhotoManager {
Assert.isMainThread();
badge.assignContactUri(parseUri(photoInfo.lookupUri()));
badge.setOverlay(null);
- LetterTileDrawable defaultDrawable = getDefaultDrawable(photoInfo);
- GlideRequest<Drawable> request =
- buildRequest(GlideApp.with(badge), photoInfo)
- .placeholder(defaultDrawable) // when the photo is still loading.
- .fallback(defaultDrawable); // when there's nothing to load.
-
- if (photoInfo.isCircular()) {
- request.circleCrop();
- }
-
+ GlideRequest<Drawable> request = buildRequest(GlideApp.with(badge), photoInfo);
request.into(badge);
}
@@ -68,21 +59,39 @@ 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.
- // Whether the number is blocked takes precedence over the spam status.
+ GlideRequest<Drawable> request;
+ boolean circleCrop = true; // Photos are cropped to a circle by default.
+
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()));
+ // Whether the number is blocked takes precedence over the spam status.
+ request = requestManager.load(R.drawable.ic_block_grey_48dp);
+
+ } else if (photoInfo.isSpam()) {
+ request = requestManager.load(R.drawable.quantum_ic_report_vd_red_24);
+ circleCrop = false; // The spam icon is an octagon so we don't crop it.
+
+ } else if (!TextUtils.isEmpty(photoInfo.photoUri())) {
+ request = requestManager.load(parseUri(photoInfo.photoUri()));
+
+ } else if (photoInfo.photoId() != 0) {
+ request =
+ requestManager.load(ContentUris.withAppendedId(Data.CONTENT_URI, photoInfo.photoId()));
+
+ } else {
+ // load null to indicate fallback should be used.
+ request = requestManager.load((Object) null);
}
- if (photoInfo.photoId() != 0) {
- return requestManager.load(ContentUris.withAppendedId(Data.CONTENT_URI, photoInfo.photoId()));
+
+ LetterTileDrawable defaultDrawable = getDefaultDrawable(photoInfo);
+ request
+ .placeholder(defaultDrawable) // when the photo is still loading.
+ .fallback(defaultDrawable); // when there's nothing to load.
+
+ if (circleCrop) {
+ request.circleCrop();
}
- // load null to indicate fallback should be used.
- return requestManager.load((Object) null);
+
+ return request;
}
/**
@@ -108,7 +117,7 @@ public class GlidePhotoManagerImpl implements GlidePhotoManager {
LetterTileDrawable.SHAPE_CIRCLE,
LetterTileDrawable.getContactTypeFromPrimitives(
photoInfo.isVoicemail(),
- false, // TODO(twyen):implement
+ photoInfo.isSpam(),
photoInfo.isBusiness(),
TelecomManager.PRESENTATION_ALLOWED, // TODO(twyen):implement
false)); // TODO(twyen):implement
diff --git a/java/com/android/dialer/logging/LoggingBindings.java b/java/com/android/dialer/logging/LoggingBindings.java
index ec98ed124..ca9a0533e 100644
--- a/java/com/android/dialer/logging/LoggingBindings.java
+++ b/java/com/android/dialer/logging/LoggingBindings.java
@@ -20,15 +20,6 @@ import android.widget.QuickContactBadge;
/** Allows the container application to gather analytics. */
public interface LoggingBindings {
- String ON_CREATE_PRIMES_EVENT_NAME = "Application.onCreate";
- String ACTIVITY_ON_CREATE_PRIMES_EVENT_NAME = "GoogleDialtactsActivity.onCreate";
- String ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING =
- "CallList.onCallAdded_To_InCallActivity.onCreate_Incoming";
- String ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING =
- "CallList.onCallAdded_To_InCallActivity.onCreate_Outgoing";
- String ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME = "GoogleDialtactsActivity.onResume";
- String INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME = "IncallActivity.OnResume";
- String INCALL_ACTIVITY_ON_STOP_MEMORY_EVENT_NAME = "IncallActivity.OnStop";
/**
* Logs an DialerImpression event that's not associated with a specific call.
*
@@ -96,13 +87,4 @@ public interface LoggingBindings {
/** Logs successful People Api lookup result */
void logSuccessfulPeopleApiLookupReport(long latency, int httpResponseCode);
-
- /** Log start a latency timer */
- void logStartLatencyTimer(String timerEventName);
-
- /** Log end a latency timer */
- void logStopLatencyTimer(String timerEventName);
-
- /** Log get a memory snapshot */
- void logRecordMemory(String memoryEventName);
}
diff --git a/java/com/android/dialer/logging/LoggingBindingsStub.java b/java/com/android/dialer/logging/LoggingBindingsStub.java
index 74ac294fc..2dbcc3ffb 100644
--- a/java/com/android/dialer/logging/LoggingBindingsStub.java
+++ b/java/com/android/dialer/logging/LoggingBindingsStub.java
@@ -61,13 +61,4 @@ public class LoggingBindingsStub implements LoggingBindings {
@Override
public void logSuccessfulPeopleApiLookupReport(long latency, int httpResponseCode) {}
-
- @Override
- public void logStartLatencyTimer(String timerEventName) {}
-
- @Override
- public void logStopLatencyTimer(String timerEventName) {}
-
- @Override
- public void logRecordMemory(String memoryEventName) {}
}
diff --git a/java/com/android/dialer/main/MainActivityPeer.java b/java/com/android/dialer/main/MainActivityPeer.java
index c1a328a65..9c5627be8 100644
--- a/java/com/android/dialer/main/MainActivityPeer.java
+++ b/java/com/android/dialer/main/MainActivityPeer.java
@@ -26,6 +26,8 @@ public interface MainActivityPeer {
void onActivityResume();
+ void onUserLeaveHint();
+
void onActivityStop();
void onActivityDestroyed();
diff --git a/java/com/android/dialer/main/impl/AndroidManifest.xml b/java/com/android/dialer/main/impl/AndroidManifest.xml
index 6b7475f97..972c9d929 100644
--- a/java/com/android/dialer/main/impl/AndroidManifest.xml
+++ b/java/com/android/dialer/main/impl/AndroidManifest.xml
@@ -29,7 +29,7 @@
android:launchMode="singleTask"
android:name="com.android.dialer.main.impl.MainActivity"
android:resizeableActivity="true"
- android:theme="@style/NuiMainActivityTheme"
+ android:theme="@style/NuiActivityTheme"
android:windowSoftInputMode="stateAlwaysHidden|adjustNothing">
diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java
index ac2cb389e..1646becf4 100644
--- a/java/com/android/dialer/main/impl/MainActivity.java
+++ b/java/com/android/dialer/main/impl/MainActivity.java
@@ -82,6 +82,7 @@ public class MainActivity extends TransactionSafeActivity
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
+ setIntent(intent);
activePeer.onNewIntent(intent);
}
@@ -92,6 +93,12 @@ public class MainActivity extends TransactionSafeActivity
}
@Override
+ protected void onUserLeaveHint() {
+ super.onUserLeaveHint();
+ activePeer.onUserLeaveHint();
+ }
+
+ @Override
protected void onStop() {
super.onStop();
activePeer.onActivityStop();
diff --git a/java/com/android/dialer/main/impl/MainSearchController.java b/java/com/android/dialer/main/impl/MainSearchController.java
index 9b734f40c..ccd7a4b49 100644
--- a/java/com/android/dialer/main/impl/MainSearchController.java
+++ b/java/com/android/dialer/main/impl/MainSearchController.java
@@ -68,8 +68,7 @@ import java.util.ArrayList;
public class MainSearchController implements SearchBarListener {
private static final String KEY_IS_FAB_HIDDEN = "is_fab_hidden";
- private static final String KEY_CURRENT_TAB = "current_tab";
- private static final String KEY_BOTTOM_NAV_VISIBILITY = "bottom_nav_visibility";
+ private static final String KEY_TOOLBAR_SHADOW_VISIBILITY = "toolbar_shadow_visibility";
private static final String KEY_IS_TOOLBAR_EXPANDED = "is_toolbar_expanded";
private static final String KEY_IS_TOOLBAR_SLIDE_UP = "is_toolbar_slide_up";
@@ -80,25 +79,30 @@ public class MainSearchController implements SearchBarListener {
private final BottomNavBar bottomNav;
private final FloatingActionButton fab;
private final MainToolbar toolbar;
+ private final View toolbarShadow;
public MainSearchController(
MainActivity mainActivity,
BottomNavBar bottomNav,
FloatingActionButton fab,
- MainToolbar toolbar) {
+ MainToolbar toolbar,
+ View toolbarShadow) {
this.mainActivity = mainActivity;
this.bottomNav = bottomNav;
this.fab = fab;
this.toolbar = toolbar;
+ this.toolbarShadow = toolbarShadow;
}
/** Should be called if we're showing the dialpad because of a new ACTION_DIAL intent. */
- public void showDialpadFromNewIntent(boolean animate) {
- showDialpad(animate, true);
+ public void showDialpadFromNewIntent() {
+ LogUtil.enterBlock("MainSearchController.showDialpadFromNewIntent");
+ showDialpad(/* animate=*/ false, /* fromNewIntent=*/ true);
}
/** Shows the dialpad, hides the FAB and slides the toolbar off screen. */
public void showDialpad(boolean animate) {
+ LogUtil.enterBlock("MainSearchController.showDialpad");
showDialpad(animate, false);
}
@@ -108,17 +112,22 @@ public class MainSearchController implements SearchBarListener {
fab.hide();
toolbar.slideUp(animate);
toolbar.expand(animate, Optional.absent());
+ toolbarShadow.setVisibility(View.VISIBLE);
mainActivity.setTitle(R.string.dialpad_activity_title);
FragmentTransaction transaction = mainActivity.getFragmentManager().beginTransaction();
+ NewSearchFragment searchFragment = getSearchFragment();
// Show Search
- if (getSearchFragment() == null) {
- NewSearchFragment searchFragment = NewSearchFragment.newInstance(false);
+ if (searchFragment == null) {
+ // TODO(a bug): zero suggest results aren't actually shown but this enabled the nearby
+ // places promo to be shown.
+ searchFragment = NewSearchFragment.newInstance(/* showZeroSuggest=*/ true);
transaction.add(R.id.fragment_container, searchFragment, SEARCH_FRAGMENT_TAG);
} else if (!isSearchVisible()) {
- transaction.show(getSearchFragment());
+ transaction.show(searchFragment);
}
+ searchFragment.setQuery("", CallInitiationType.Type.DIALPAD);
// Show Dialpad
if (getDialpadFragment() == null) {
@@ -143,6 +152,7 @@ public class MainSearchController implements SearchBarListener {
* @see {@link #closeSearch(boolean)} to "remove" the dialpad.
*/
private void hideDialpad(boolean animate, boolean bottomNavVisible) {
+ LogUtil.enterBlock("MainSearchController.hideDialpad");
Assert.checkArgument(isDialpadVisible());
fab.show();
@@ -188,6 +198,7 @@ public class MainSearchController implements SearchBarListener {
/** Should be called when {@link DialpadListener#onDialpadShown()} is called. */
public void onDialpadShown() {
+ LogUtil.enterBlock("MainSearchController.onDialpadShown");
getDialpadFragment().slideUp(true);
hideBottomNav();
}
@@ -203,6 +214,7 @@ public class MainSearchController implements SearchBarListener {
* </ol>
*/
public void onSearchListTouch() {
+ LogUtil.enterBlock("MainSearchController.onSearchListTouched");
if (isDialpadVisible()) {
if (TextUtils.isEmpty(getDialpadFragment().getQuery())) {
Logger.get(mainActivity)
@@ -234,13 +246,13 @@ public class MainSearchController implements SearchBarListener {
*/
public boolean onBackPressed() {
if (isDialpadVisible() && !TextUtils.isEmpty(getDialpadFragment().getQuery())) {
- LogUtil.i("MainSearchController#onBackPressed", "Dialpad visible with query");
+ 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");
+ LogUtil.i("MainSearchController.onBackPressed", "Search is visible");
Logger.get(mainActivity)
.logImpression(
isDialpadVisible()
@@ -258,6 +270,7 @@ public class MainSearchController implements SearchBarListener {
* dialpad.
*/
private void closeSearch(boolean animate) {
+ LogUtil.enterBlock("MainSearchController.closeSearch");
Assert.checkArgument(isSearchVisible());
if (isDialpadVisible()) {
hideDialpad(animate, /* bottomNavVisible=*/ true);
@@ -266,6 +279,7 @@ public class MainSearchController implements SearchBarListener {
}
showBottomNav();
toolbar.collapse(animate);
+ toolbarShadow.setVisibility(View.GONE);
mainActivity.getFragmentManager().beginTransaction().remove(getSearchFragment()).commit();
// Clear the dialpad so the phone number isn't persisted between search sessions.
@@ -311,31 +325,40 @@ public class MainSearchController implements SearchBarListener {
*/
@Override
public void onSearchBarClicked() {
+ LogUtil.enterBlock("MainSearchController.onSearchBarClicked");
Logger.get(mainActivity).logImpression(DialerImpression.Type.NUI_CLICK_SEARCH_BAR);
openSearch(Optional.absent());
}
private void openSearch(Optional<String> query) {
+ LogUtil.enterBlock("MainSearchController.openSearch");
fab.hide();
toolbar.expand(/* animate=*/ true, query);
toolbar.showKeyboard();
+ toolbarShadow.setVisibility(View.VISIBLE);
hideBottomNav();
FragmentTransaction transaction = mainActivity.getFragmentManager().beginTransaction();
+ NewSearchFragment searchFragment = getSearchFragment();
// Show Search
- if (getSearchFragment() == null) {
- NewSearchFragment searchFragment = NewSearchFragment.newInstance(false);
+ if (searchFragment == null) {
+ // TODO(a bug): zero suggest results aren't actually shown but this enabled the nearby
+ // places promo to be shown.
+ searchFragment = NewSearchFragment.newInstance(true);
transaction.add(R.id.fragment_container, searchFragment, SEARCH_FRAGMENT_TAG);
} else if (!isSearchVisible()) {
transaction.show(getSearchFragment());
}
+ searchFragment.setQuery(
+ query.isPresent() ? query.get() : "", CallInitiationType.Type.REGULAR_SEARCH);
transaction.commit();
}
@Override
public void onSearchBackButtonClicked() {
+ LogUtil.enterBlock("MainSearchController.onSearchBackButtonClicked");
closeSearch(true);
}
@@ -380,6 +403,18 @@ public class MainSearchController implements SearchBarListener {
return false;
}
+ @Override
+ public void onUserLeaveHint() {
+ if (isInSearch()) {
+ closeSearch(false);
+ }
+ }
+
+ @Override
+ public void onCallPlacedFromSearch() {
+ closeSearch(false);
+ }
+
public void onVoiceResults(int resultCode, Intent data) {
if (resultCode == AppCompatActivity.RESULT_OK) {
ArrayList<String> matches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
@@ -396,15 +431,13 @@ public class MainSearchController implements SearchBarListener {
public void onSaveInstanceState(Bundle bundle) {
bundle.putBoolean(KEY_IS_FAB_HIDDEN, !fab.isShown());
- bundle.putInt(KEY_CURRENT_TAB, bottomNav.getSelectedTab());
- bundle.putInt(KEY_BOTTOM_NAV_VISIBILITY, bottomNav.getVisibility());
+ bundle.putInt(KEY_TOOLBAR_SHADOW_VISIBILITY, toolbarShadow.getVisibility());
bundle.putBoolean(KEY_IS_TOOLBAR_EXPANDED, toolbar.isExpanded());
bundle.putBoolean(KEY_IS_TOOLBAR_SLIDE_UP, toolbar.isSlideUp());
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
- bottomNav.selectTab(savedInstanceState.getInt(KEY_CURRENT_TAB));
- bottomNav.setVisibility(savedInstanceState.getInt(KEY_BOTTOM_NAV_VISIBILITY));
+ toolbarShadow.setVisibility(savedInstanceState.getInt(KEY_TOOLBAR_SHADOW_VISIBILITY));
if (savedInstanceState.getBoolean(KEY_IS_FAB_HIDDEN, false)) {
fab.hide();
}
diff --git a/java/com/android/dialer/main/impl/NewMainActivityPeer.java b/java/com/android/dialer/main/impl/NewMainActivityPeer.java
index ed67df936..6f5c18623 100644
--- a/java/com/android/dialer/main/impl/NewMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/NewMainActivityPeer.java
@@ -57,6 +57,9 @@ public class NewMainActivityPeer implements MainActivityPeer {
public void onActivityResume() {}
@Override
+ public void onUserLeaveHint() {}
+
+ @Override
public void onActivityStop() {}
@Override
diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
index 07c7185ae..8762f5889 100644
--- a/java/com/android/dialer/main/impl/OldMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java
@@ -30,16 +30,24 @@ import android.provider.CallLog.Calls;
import android.provider.ContactsContract.QuickContact;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
+import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.app.DialtactsActivity;
import com.android.dialer.app.calllog.CallLogAdapter;
import com.android.dialer.app.calllog.CallLogFragment;
import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener;
import com.android.dialer.app.calllog.CallLogNotificationsService;
+import com.android.dialer.app.calllog.IntentProvider;
import com.android.dialer.app.calllog.VisualVoicemailCallLogFragment;
import com.android.dialer.app.list.DragDropController;
import com.android.dialer.app.list.OldSpeedDialFragment;
@@ -47,6 +55,8 @@ import com.android.dialer.app.list.OnDragDropListener;
import com.android.dialer.app.list.OnListFragmentScrolledListener;
import com.android.dialer.app.list.PhoneFavoriteSquareTileView;
import com.android.dialer.app.list.RemoveView;
+import com.android.dialer.callcomposer.CallComposerActivity;
+import com.android.dialer.calldetails.CallDetailsActivity;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.callintent.CallSpecificAppData;
import com.android.dialer.common.FragmentUtils.FragmentUtilListener;
@@ -65,6 +75,7 @@ import com.android.dialer.dialpadview.DialpadFragment;
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.duo.DuoComponent;
import com.android.dialer.interactions.PhoneNumberInteraction;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
@@ -81,6 +92,7 @@ import com.android.dialer.storage.StorageComponent;
import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.TransactionSafeActivity;
+import com.android.voicemail.VoicemailComponent;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.TimeUnit;
@@ -190,13 +202,18 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
MainBottomNavBarBottomNavTabListener bottomNavTabListener =
new MainBottomNavBarBottomNavTabListener(mainActivity, mainActivity.getFragmentManager());
bottomNav.addOnTabSelectedListener(bottomNavTabListener);
+ // TODO(uabdullah): Handle case of when a sim is inserted/removed while the activity is open.
+ boolean showVoicemailTab = canVoicemailTabBeShown(mainActivity);
+ bottomNav.showVoicemail(showVoicemailTab);
callLogFragmentListener =
new MainCallLogFragmentListener(
mainActivity, mainActivity.getContentResolver(), bottomNav, toolbar);
bottomNav.addOnTabSelectedListener(callLogFragmentListener);
- searchController = getNewMainSearchController(bottomNav, fab, toolbar);
+ searchController =
+ getNewMainSearchController(
+ bottomNav, fab, toolbar, mainActivity.findViewById(R.id.toolbar_shadow));
toolbar.setSearchBarListener(searchController);
onDialpadQueryChangedListener = getNewOnDialpadQueryChangedListener(searchController);
@@ -216,7 +233,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
mainActivity.findViewById(R.id.remove_view),
mainActivity.findViewById(R.id.search_view_container));
- lastTabController = new LastTabController(mainActivity, bottomNav);
+ lastTabController = new LastTabController(mainActivity, bottomNav, showVoicemailTab);
// Restore our view state if needed, else initialize as if the app opened for the first time
if (savedInstanceState != null) {
@@ -224,39 +241,119 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
searchController.onRestoreInstanceState(savedInstanceState);
bottomNav.selectTab(savedInstanceState.getInt(KEY_CURRENT_TAB));
} else {
- showTabOnIntent(mainActivity.getIntent());
+ onHandleIntent(mainActivity.getIntent());
}
}
+ /**
+ * Check and return whether the voicemail tab should be shown or not. This includes the following
+ * criteria under which we show the voicemail tab:
+ * <li>The voicemail number exists (e.g we are able to dial into listen to voicemail or press and
+ * hold 1)
+ * <li>Visual voicemail is enabled from the settings tab
+ * <li>Visual voicemail carrier is supported by dialer
+ * <li>There is no voicemail carrier app installed.
+ *
+ * @param context
+ * @return return if voicemail tab should be shown or not depending on what the voicemail state is
+ * for the carrier.
+ */
+ private static boolean canVoicemailTabBeShown(Context context) {
+ PhoneAccountHandle defaultUserSelectedAccount =
+ TelecomUtil.getDefaultOutgoingPhoneAccount(context, PhoneAccount.SCHEME_VOICEMAIL);
+
+ if (isVoicemailAvailable(context, defaultUserSelectedAccount)) {
+ return true;
+ }
+ if (VoicemailComponent.get(context)
+ .getVoicemailClient()
+ .isVoicemailEnabled(context, defaultUserSelectedAccount)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if voicemail is enabled/accessible.
+ *
+ * @return true if voicemail is enabled and accessible. Note that this can be false "temporarily"
+ * after the app boot e.g if the sim isn't fully recognized. TODO(uabdullah): Possibly add a
+ * listener of some kind to detect when a sim is recognized. TODO(uabdullah): Move this to a
+ * utility class or wrap it all in a static inner class.
+ */
+ private static boolean isVoicemailAvailable(
+ Context context, PhoneAccountHandle defaultUserSelectedAccount) {
+
+ if (!TelecomUtil.hasReadPhoneStatePermission(context)) {
+ LogUtil.i(
+ "OldMainActivityPeer.isVoicemailAvailable",
+ "No read phone permisison or not the default dialer.");
+ return false;
+ }
+
+ if (defaultUserSelectedAccount == null) {
+ // In a single-SIM phone, there is no default outgoing phone account selected by
+ // the user, so just call TelephonyManager#getVoicemailNumber directly.
+ return !TextUtils.isEmpty(getTelephonyManager(context).getVoiceMailNumber());
+ } else {
+ return !TextUtils.isEmpty(
+ TelecomUtil.getVoicemailNumber(context, defaultUserSelectedAccount));
+ }
+ }
+
+ private static TelephonyManager getTelephonyManager(Context context) {
+ return context.getSystemService(TelephonyManager.class);
+ }
+
@Override
public void onNewIntent(Intent intent) {
LogUtil.enterBlock("OldMainActivityPeer.onNewIntent");
- showTabOnIntent(intent);
+ onHandleIntent(intent);
}
- private void showTabOnIntent(Intent intent) {
- if (isShowTabIntent(intent)) {
+ private void onHandleIntent(Intent intent) {
+ // Some important implementation notes:
+ // 1) If the intent contains extra data to open to a specific screen (e.g. DIAL intent), when
+ // the user leaves that screen, they will return here and add see a blank screen unless we
+ // select a tab here.
+ // 2) Don't return early here in case the intent does contain extra data.
+ // 3) External intents should take priority over other intents (like Calls.CONTENT_TYPE).
+ if (Calls.CONTENT_TYPE.equals(intent.getType())) {
+ Bundle extras = intent.getExtras();
+ if (extras != null && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "Voicemail content type intent");
+ bottomNav.selectTab(TabIndex.VOICEMAIL);
+ Logger.get(mainActivity).logImpression(DialerImpression.Type.VVM_NOTIFICATION_CLICKED);
+ } else {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "Call log content type intent");
+ bottomNav.selectTab(TabIndex.CALL_LOG);
+ }
+
+ } else if (isShowTabIntent(intent)) {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "Show tab intent");
bottomNav.selectTab(getTabFromIntent(intent));
- return;
+ } else if (lastTabController.isEnabled) {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "Show last tab");
+ lastTabController.selectLastTab();
+ } else {
+ bottomNav.selectTab(TabIndex.SPEED_DIAL);
}
- if (isDialIntent(intent)) {
- searchController.showDialpadFromNewIntent(false);
+ if (isDialOrAddCallIntent(intent)) {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "Dial or add call intent");
// Dialpad will grab the intent and populate the number
- return;
+ searchController.showDialpadFromNewIntent();
}
- if (lastTabController.isEnabled) {
- lastTabController.selectLastTab();
- return;
+ if (intent.getBooleanExtra(DialtactsActivity.EXTRA_CLEAR_NEW_VOICEMAILS, false)) {
+ LogUtil.i("OldMainActivityPeer.onHandleIntent", "clearing all new voicemails");
+ CallLogNotificationsService.markAllNewVoicemailsAsOld(mainActivity);
}
-
- bottomNav.selectTab(TabIndex.SPEED_DIAL);
}
- /** Returns true if the given intent contains a phone number to populate the dialer with */
- private boolean isDialIntent(Intent intent) {
- if (intent == null || intent.getData() == null) {
+ /** Returns true if the given intent is a Dial intent with data or an Add Call intent. */
+ private boolean isDialOrAddCallIntent(Intent intent) {
+ if (intent == null) {
return false;
}
@@ -270,7 +367,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
return true;
}
}
- return false;
+ return DialpadFragment.isAddCallMode(intent);
}
@Override
@@ -284,6 +381,18 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
.getDatabaseHelper(mainActivity)
.startSmartDialUpdateThread(forceUpdate);
showPostCallPrompt();
+
+ if (searchController.isInSearch()
+ || callLogAdapterOnActionModeStateChangedListener.isActionModeStateEnabled()) {
+ bottomNav.setVisibility(View.GONE);
+ } else {
+ bottomNav.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onUserLeaveHint() {
+ searchController.onUserLeaveHint();
}
@Override
@@ -328,6 +437,44 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
resultCode);
if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) {
searchController.onVoiceResults(resultCode, data);
+ } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_COMPOSER) {
+ if (resultCode == AppCompatActivity.RESULT_FIRST_USER) {
+ LogUtil.i(
+ "OldMainActivityPeer.onActivityResult", "returned from call composer, error occurred");
+ String message =
+ mainActivity.getString(
+ R.string.call_composer_connection_failed,
+ data.getStringExtra(CallComposerActivity.KEY_CONTACT_NAME));
+ Snackbar.make(snackbarContainer, message, Snackbar.LENGTH_LONG).show();
+ } else {
+ LogUtil.i("OldMainActivityPeer.onActivityResult", "returned from call composer, no error");
+ }
+
+ } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
+ if (resultCode == AppCompatActivity.RESULT_OK
+ && data != null
+ && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
+ String number = data.getStringExtra(CallDetailsActivity.EXTRA_PHONE_NUMBER);
+ int snackbarDurationMillis = 5_000;
+ Snackbar.make(
+ snackbarContainer,
+ mainActivity.getString(R.string.ec_data_deleted),
+ snackbarDurationMillis)
+ .setAction(
+ R.string.view_conversation,
+ v ->
+ mainActivity.startActivity(
+ IntentProvider.getSendSmsIntentProvider(number).getIntent(mainActivity)))
+ .setActionTextColor(
+ ContextCompat.getColor(mainActivity, R.color.dialer_snackbar_action_text_color))
+ .show();
+ }
+
+ } else if (requestCode == ActivityRequestCodes.DIALTACTS_DUO) {
+ // We just returned from starting Duo for a task. Reload our reachability data since it
+ // may have changed after a user finished activating Duo.
+ DuoComponent.get(mainActivity).getDuo().reloadReachability(mainActivity);
+
} else {
LogUtil.e("OldMainActivityPeer.onActivityResult", "Unknown request code: " + requestCode);
}
@@ -335,6 +482,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
@Override
public boolean onBackPressed() {
+ LogUtil.enterBlock("OldMainActivityPeer.onBackPressed");
if (searchController.onBackPressed()) {
return true;
}
@@ -375,8 +523,11 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
}
public MainSearchController getNewMainSearchController(
- BottomNavBar bottomNavBar, FloatingActionButton fab, MainToolbar mainToolbar) {
- return new MainSearchController(mainActivity, bottomNavBar, fab, mainToolbar);
+ BottomNavBar bottomNavBar,
+ FloatingActionButton fab,
+ MainToolbar mainToolbar,
+ View toolbarShadow) {
+ return new MainSearchController(mainActivity, bottomNavBar, fab, mainToolbar, toolbarShadow);
}
public MainOnDialpadQueryChangedListener getNewOnDialpadQueryChangedListener(
@@ -448,6 +599,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
@Override
public void onCallPlacedFromDialpad() {
// TODO(calderwoodra): logging
+ searchController.onCallPlacedFromSearch();
}
}
@@ -468,6 +620,7 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
@Override
public void onCallPlacedFromSearch() {
// TODO(calderwoodra): logging
+ searchController.onCallPlacedFromSearch();
}
}
@@ -482,7 +635,6 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
}
/** @see CallLogAdapter.OnActionModeStateChangedListener */
- // TODO(calderwoodra): What is the purpose of this listener?
private static final class MainCallLogAdapterOnActionModeStateChangedListener
implements CallLogAdapter.OnActionModeStateChangedListener {
@@ -928,11 +1080,13 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
private final Context context;
private final BottomNavBar bottomNavBar;
private final boolean isEnabled;
+ private final boolean canShowVoicemailTab;
- LastTabController(Context context, BottomNavBar bottomNavBar) {
+ LastTabController(Context context, BottomNavBar bottomNavBar, boolean canShowVoicemailTab) {
this.context = context;
this.bottomNavBar = bottomNavBar;
isEnabled = ConfigProviderBindings.get(context).getBoolean("last_tab_enabled", false);
+ this.canShowVoicemailTab = canShowVoicemailTab;
}
/** Sets the last tab if the feature is enabled, otherwise defaults to speed dial. */
@@ -944,6 +1098,12 @@ public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListen
.unencryptedSharedPrefs()
.getInt(KEY_LAST_TAB, TabIndex.SPEED_DIAL);
}
+
+ // If the voicemail tab cannot be shown, default to showing speed dial
+ if (tabIndex == TabIndex.VOICEMAIL && !canShowVoicemailTab) {
+ tabIndex = TabIndex.SPEED_DIAL;
+ }
+
bottomNavBar.selectTab(tabIndex);
}
diff --git a/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java b/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
index 2945e39a9..d9a446f84 100644
--- a/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
+++ b/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
@@ -123,6 +123,10 @@ public final class BottomNavBar extends LinearLayout {
}
}
+ public void showVoicemail(boolean showTab) {
+ voicemail.setVisibility(showTab ? View.VISIBLE : View.GONE);
+ }
+
public void setNotificationCount(@TabIndex int tab, int count) {
if (tab == TabIndex.SPEED_DIAL) {
speedDial.setNotificationCount(count);
diff --git a/java/com/android/dialer/main/impl/res/layout/main_activity.xml b/java/com/android/dialer/main/impl/res/layout/main_activity.xml
index d8b13a6c2..4f0284126 100644
--- a/java/com/android/dialer/main/impl/res/layout/main_activity.xml
+++ b/java/com/android/dialer/main/impl/res/layout/main_activity.xml
@@ -68,11 +68,13 @@
layout="@layout/toolbar_layout"/>
<ImageView
+ android:id="@+id/toolbar_shadow"
android:layout_width="match_parent"
android:layout_height="2dp"
android:scaleType="fitXY"
android:src="@drawable/search_shadow"
- android:layout_below="@+id/toolbar"/>
+ android:layout_below="@+id/toolbar"
+ android:visibility="gone"/>
<!-- TODO(calderwoodra): investigate what this is for and why we want it. -->
<!-- Host container for the contact tile drag shadow -->
diff --git a/java/com/android/dialer/glidephotomanager/impl/res/drawable/ic_report_red_48dp.xml b/java/com/android/dialer/main/impl/res/values-v27/styles.xml
index 8a1ddcd99..c91cba245 100644
--- a/java/com/android/dialer/glidephotomanager/impl/res/drawable/ic_report_red_48dp.xml
+++ b/java/com/android/dialer/main/impl/res/values-v27/styles.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
~ Copyright (C) 2018 The Android Open Source Project
~
@@ -15,22 +14,13 @@
~ See the License for the specific language governing permissions and
~ limitations under the License
-->
+<resources>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item>
- <shape android:shape="oval">
- <solid android:color="#A52714"/>
- <size
- android:height="48dp"
- android:width="48dp"/>
- </shape>
- </item>
-
- <item
- android:drawable="@drawable/quantum_ic_report_vd_theme_24"
- android:gravity="center"
- android:height="36dp"
- android:width="36dp"/>
-
-</layer-list> \ No newline at end of file
+ <!-- Used on sdk 27 and above -->
+ <style name="NuiActivityTheme" parent="NuiActivityThemeBase">
+ <!-- Used to change the navigation bar color -->
+ <item name="android:windowLightNavigationBar">true</item>
+ <item name="android:navigationBarColor">?android:windowBackground</item>
+ <item name="android:navigationBarDividerColor">#E0E0E0</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/main/impl/res/values/styles.xml b/java/com/android/dialer/main/impl/res/values/styles.xml
index 2865f2587..47fdbac93 100644
--- a/java/com/android/dialer/main/impl/res/values/styles.xml
+++ b/java/com/android/dialer/main/impl/res/values/styles.xml
@@ -15,14 +15,35 @@
~ limitations under the License
-->
<resources>
- <style name="NuiMainActivityTheme" parent="Theme.AppCompat.Light.NoActionBar">
+
+ <!-- Activities should use this theme as their style -->
+ <style name="NuiActivityTheme" parent="NuiActivityThemeBase"/>
+
+ <!-- Used as a procy for values-v27/styles.xml -->
+ <style name="NuiActivityThemeBase" parent="Theme.AppCompat.Light.NoActionBar">
+ <!-- App colors -->
<item name="android:colorPrimary">@color/dialtacts_theme_color</item>
<item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
<item name="android:colorAccent">@color/dialer_secondary_color</item>
+ <!-- TODO(calderwoodra): figure out what this is used for, but I think it's for checkboxes -->
+ <item name="android:colorControlActivated">@color/dialer_theme_color</item>
+
+ <!-- Text colors -->
+ <item name="android:textColorPrimary">@color/dialer_primary_text_color</item>
+ <item name="android:textColorSecondary">@color/dialer_secondary_text_color</item>
+ <item name="android:textColorLink">@color/dialer_theme_color</item>
+
+ <!-- Themeing for material buttons and widgets -->
+ <item name="android:colorButtonNormal">@color/dialer_theme_color</item>
+ <item name="android:textAppearanceButton">@style/DialerButtonTextStyle</item>
+
<!-- Theme needed for DialpadFragment -->
<item name="dialpad_style">@style/Dialpad.Light</item>
+ <!-- Custom theme for Alert Dialogs-->
+ <item name="android:alertDialogTheme">@style/AlertDialogTheme</item>
+
<!-- Required for actionmode/multiselect to render properly. -->
<!-- TODO(calderwoodra): Check to see if we can remove this after NewVoicemailFragment launches -->
<item name="actionModeStyle">@style/ActionModeStyle</item>
diff --git a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
index 604422978..a129fca8b 100644
--- a/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
+++ b/java/com/android/dialer/main/impl/toolbar/MainToolbar.java
@@ -16,13 +16,17 @@
package com.android.dialer.main.impl.toolbar;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
+import android.support.annotation.StringRes;
import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.view.MenuItem;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageButton;
+import android.widget.RelativeLayout;
import com.android.dialer.common.Assert;
import com.android.dialer.util.ViewUtil;
import com.google.common.base.Optional;
@@ -74,22 +78,40 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene
return;
}
isSlideUp = true;
- animate()
- .translationY(-getHeight())
- .setDuration(animate ? SLIDE_DURATION : 0)
- .setInterpolator(SLIDE_INTERPOLATOR)
- .start();
+ ValueAnimator animator = ValueAnimator.ofFloat(0, -getHeight());
+ animator.setDuration(animate ? SLIDE_DURATION : 0);
+ animator.setInterpolator(SLIDE_INTERPOLATOR);
+ animator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int val = ((Float) animation.getAnimatedValue()).intValue();
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
+ params.topMargin = val;
+ requestLayout();
+ }
+ });
+ animator.start();
}
/** Slides the toolbar down and back onto the screen. */
public void slideDown(boolean animate) {
Assert.checkArgument(isSlideUp);
isSlideUp = false;
- animate()
- .translationY(0)
- .setDuration(animate ? SLIDE_DURATION : 0)
- .setInterpolator(SLIDE_INTERPOLATOR)
- .start();
+ ValueAnimator animator = ValueAnimator.ofFloat(-getHeight(), 0);
+ animator.setDuration(animate ? SLIDE_DURATION : 0);
+ animator.setInterpolator(SLIDE_INTERPOLATOR);
+ animator.addUpdateListener(
+ new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int val = ((Float) animation.getAnimatedValue()).intValue();
+ RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) getLayoutParams();
+ params.topMargin = val;
+ requestLayout();
+ }
+ });
+ animator.start();
}
/** @see SearchBarView#collapse(boolean) */
@@ -129,4 +151,8 @@ public final class MainToolbar extends Toolbar implements OnMenuItemClickListene
public MainToolbarMenu getOverflowMenu() {
return overflowMenu;
}
+
+ public void setHint(@StringRes int hint) {
+ searchBar.setHint(hint);
+ }
}
diff --git a/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java b/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java
index a074b5131..857c4b9c9 100644
--- a/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java
+++ b/java/com/android/dialer/main/impl/toolbar/SearchBarListener.java
@@ -16,6 +16,7 @@
package com.android.dialer.main.impl.toolbar;
+import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
/** Useful callback for {@link SearchBarView} listeners. */
@@ -36,6 +37,12 @@ public interface SearchBarListener {
/** Called when a toolbar menu item is clicked. */
boolean onMenuItemClicked(MenuItem menuItem);
+ /** Called when {@link AppCompatActivity#onUserLeaveHint()} is called. */
+ void onUserLeaveHint();
+
+ /** Called when the user places a call from search (regular or dialpad). */
+ void onCallPlacedFromSearch();
+
/** Interface for returning voice results to the search bar. */
interface VoiceSearchResultCallback {
diff --git a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java
index 95929383b..37ffb9778 100644
--- a/java/com/android/dialer/main/impl/toolbar/SearchBarView.java
+++ b/java/com/android/dialer/main/impl/toolbar/SearchBarView.java
@@ -22,6 +22,7 @@ import android.animation.ValueAnimator;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -29,6 +30,7 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.EditText;
import android.widget.FrameLayout;
+import android.widget.TextView;
import com.android.dialer.animation.AnimUtils;
import com.android.dialer.common.UiUtil;
import com.android.dialer.util.DialerUtils;
@@ -42,14 +44,15 @@ final class SearchBarView extends FrameLayout {
private final float margin;
private final float animationEndHeight;
+ private final float animationStartHeight;
private SearchBarListener listener;
private EditText searchBox;
+ private TextView searchBoxTextView;
// This useful for when the query didn't actually change. We want to avoid making excessive calls
// where we can since IPCs can take a long time on slow networks.
private boolean skipLatestTextChange;
- private int initialHeight;
private boolean isExpanded;
private View searchBoxCollapsed;
private View searchBoxExpanded;
@@ -60,6 +63,8 @@ final class SearchBarView extends FrameLayout {
margin = getContext().getResources().getDimension(R.dimen.search_bar_margin);
animationEndHeight =
getContext().getResources().getDimension(R.dimen.expanded_search_bar_height);
+ animationStartHeight =
+ getContext().getResources().getDimension(R.dimen.collapsed_search_bar_height);
}
@Override
@@ -67,6 +72,7 @@ final class SearchBarView extends FrameLayout {
super.onFinishInflate();
clearButton = findViewById(R.id.search_clear_button);
searchBox = findViewById(R.id.search_view);
+ searchBoxTextView = findViewById(R.id.search_box_start_search);
searchBoxCollapsed = findViewById(R.id.search_box_collapsed);
searchBoxExpanded = findViewById(R.id.search_box_expanded);
@@ -104,7 +110,6 @@ final class SearchBarView extends FrameLayout {
if (isExpanded) {
return;
}
- initialHeight = getHeight();
int duration = animate ? ANIMATION_DURATION : 0;
searchBoxExpanded.setVisibility(VISIBLE);
@@ -177,7 +182,7 @@ final class SearchBarView extends FrameLayout {
params.leftMargin = margin;
params.rightMargin = margin;
searchBoxExpanded.getLayoutParams().height =
- (int) (animationEndHeight - (animationEndHeight - initialHeight) * fraction);
+ (int) (animationEndHeight - (animationEndHeight - animationStartHeight) * fraction);
requestLayout();
}
@@ -207,6 +212,11 @@ final class SearchBarView extends FrameLayout {
UiUtil.openKeyboardFrom(getContext(), searchBox);
}
+ public void setHint(@StringRes int hint) {
+ searchBox.setHint(hint);
+ searchBoxTextView.setText(hint);
+ }
+
/** Handles logic for text changes in the search box. */
private class SearchBoxTextWatcher implements TextWatcher {
diff --git a/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml b/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
index 4e49accae..3bd71b63a 100644
--- a/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
+++ b/java/com/android/dialer/main/impl/toolbar/res/layout/expanded_search_bar.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_box_expanded"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/expanded_search_bar_height"
android:visibility="invisible">
<ImageButton
diff --git a/java/com/android/dialer/main/impl/toolbar/res/layout/toolbar_layout.xml b/java/com/android/dialer/main/impl/toolbar/res/layout/toolbar_layout.xml
index 59cc35451..378b20f47 100644
--- a/java/com/android/dialer/main/impl/toolbar/res/layout/toolbar_layout.xml
+++ b/java/com/android/dialer/main/impl/toolbar/res/layout/toolbar_layout.xml
@@ -17,7 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
+ android:layout_height="@dimen/expanded_search_bar_height"
android:background="@color/dialer_theme_color"
app:contentInsetStart="0dp"
app:contentInsetEnd="0dp">
@@ -27,6 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/search_bar_margin"
+ android:minHeight="@dimen/collapsed_search_bar_height"
android:background="@drawable/rounded_corner"
android:elevation="4dp">
@@ -58,8 +59,8 @@
android:layout_marginStart="8dp"
android:layout_centerVertical="true"
android:fontFamily="sans-serif"
- android:hint="@string/dialer_hint_find_contact"
- android:textColorHint="@color/dialer_secondary_text_color"
+ android:text="@string/dialer_hint_find_contact"
+ android:textColor="@color/dialer_secondary_text_color"
android:textSize="16dp"/>
<ImageView
@@ -128,4 +129,4 @@
android:textSize="16sp"/>
</LinearLayout>
</com.android.dialer.app.list.RemoveView>
-</com.android.dialer.main.impl.toolbar.MainToolbar> \ No newline at end of file
+</com.android.dialer.main.impl.toolbar.MainToolbar>
diff --git a/java/com/android/dialer/main/impl/toolbar/res/values/dimens.xml b/java/com/android/dialer/main/impl/toolbar/res/values/dimens.xml
index f54f053da..ed6f197fa 100644
--- a/java/com/android/dialer/main/impl/toolbar/res/values/dimens.xml
+++ b/java/com/android/dialer/main/impl/toolbar/res/values/dimens.xml
@@ -16,5 +16,7 @@
-->
<resources>
<dimen name="search_bar_margin">8dp</dimen>
- <dimen name="expanded_search_bar_height">60dp</dimen>
+ <dimen name="collapsed_search_bar_height">48dp</dimen>
+ <!-- collapsed + margin * 2 -->
+ <dimen name="expanded_search_bar_height">64dp</dimen>
</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/metrics/Metrics.java b/java/com/android/dialer/metrics/Metrics.java
index 3922a8cfa..9488f3068 100644
--- a/java/com/android/dialer/metrics/Metrics.java
+++ b/java/com/android/dialer/metrics/Metrics.java
@@ -17,17 +17,34 @@
package com.android.dialer.metrics;
import android.app.Application;
-import android.content.Context;
/** Logs metrics. */
public interface Metrics {
+ String APPLICATION_ON_CREATE_EVENT_NAME = "Application.onCreate";
+ String DIALTACTS_ON_CREATE_EVENT_NAME = "GoogleDialtactsActivity.onCreate";
+ String ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING =
+ "CallList.onCallAdded_To_InCallActivity.onCreate_Incoming";
+ String ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING =
+ "CallList.onCallAdded_To_InCallActivity.onCreate_Outgoing";
+ String DIALTACTS_ON_RESUME_MEMORY_EVENT_NAME = "GoogleDialtactsActivity.onResume";
+ String INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME = "IncallActivity.OnResume";
+ String INCALL_ACTIVITY_ON_STOP_MEMORY_EVENT_NAME = "IncallActivity.OnStop";
+ String OLD_CALL_LOG_JANK_EVENT_NAME = "OldCallLog.Jank";
+ String NEW_CALL_LOG_JANK_EVENT_NAME = "NewCallLog.Jank";
+
/** Start a timer. */
- void startTimer(Context context, String timerEventName);
+ void startTimer(String timerEventName);
/** Stop a timer. */
void stopTimer(String timerEventName);
+ /** Start a jank recorder. */
+ void startJankRecorder(String eventName);
+
+ /** Stop a jank recorder. */
+ void stopJankRecorder(String eventName);
+
/** Record memory. */
void recordMemory(String memoryEventName);
diff --git a/java/com/android/dialer/metrics/StubMetrics.java b/java/com/android/dialer/metrics/StubMetrics.java
index 114eb4308..99c3d7691 100644
--- a/java/com/android/dialer/metrics/StubMetrics.java
+++ b/java/com/android/dialer/metrics/StubMetrics.java
@@ -16,7 +16,6 @@
package com.android.dialer.metrics;
-import android.content.Context;
import javax.inject.Inject;
/** Stub {@link Metrics}. */
@@ -26,11 +25,17 @@ public final class StubMetrics implements Metrics {
StubMetrics() {}
@Override
- public void startTimer(Context context, String timerEventName) {}
+ public void startTimer(String timerEventName) {}
@Override
public void stopTimer(String timerEventName) {}
@Override
+ public void startJankRecorder(String eventName) {}
+
+ @Override
+ public void stopJankRecorder(String eventName) {}
+
+ @Override
public void recordMemory(String memoryEventName) {}
}
diff --git a/java/com/android/dialer/metrics/jank/RecyclerViewJankLogger.java b/java/com/android/dialer/metrics/jank/RecyclerViewJankLogger.java
new file mode 100644
index 000000000..c9f389285
--- /dev/null
+++ b/java/com/android/dialer/metrics/jank/RecyclerViewJankLogger.java
@@ -0,0 +1,46 @@
+/*
+ * 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.jank;
+
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import com.android.dialer.metrics.Metrics;
+
+/** Logs jank for {@link RecyclerView} scrolling events. */
+public final class RecyclerViewJankLogger extends OnScrollListener {
+
+ private final Metrics metrics;
+ private final String eventName;
+
+ private boolean isScrolling;
+
+ public RecyclerViewJankLogger(Metrics metrics, String eventName) {
+ this.metrics = metrics;
+ this.eventName = eventName;
+ }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ if (!isScrolling && newState == RecyclerView.SCROLL_STATE_DRAGGING) {
+ isScrolling = true;
+ metrics.startJankRecorder(eventName);
+ } else if (isScrolling && newState == RecyclerView.SCROLL_STATE_IDLE) {
+ isScrolling = false;
+ metrics.stopJankRecorder(eventName);
+ }
+ }
+}
diff --git a/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java b/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java
index 79abff08e..81f6b607c 100644
--- a/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java
+++ b/java/com/android/dialer/oem/MotorolaHiddenMenuKeySequence.java
@@ -22,7 +22,11 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.support.annotation.VisibleForTesting;
import com.android.dialer.common.LogUtil;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.regex.Pattern;
/**
@@ -30,13 +34,14 @@ import java.util.regex.Pattern;
*/
public class MotorolaHiddenMenuKeySequence {
private static final String EXTRA_HIDDEN_MENU_CODE = "HiddenMenuCode";
+
private static MotorolaHiddenMenuKeySequence instance = null;
- private static String[] hiddenKeySequenceArray = null;
- private static String[] hiddenKeySequenceIntentArray = null;
- private static String[] hiddenKeyPatternArray = null;
- private static String[] hiddenKeyPatternIntentArray = null;
- private static boolean featureHiddenMenuEnabled = false;
+ @VisibleForTesting final List<String> hiddenKeySequences = new ArrayList<>();
+ @VisibleForTesting final List<String> hiddenKeySequenceIntents = new ArrayList<>();
+ @VisibleForTesting final List<String> hiddenKeyPatterns = new ArrayList<>();
+ @VisibleForTesting final List<String> hiddenKeyPatternIntents = new ArrayList<>();
+ @VisibleForTesting boolean featureHiddenMenuEnabled = false;
/**
* Handle input char sequence.
@@ -46,8 +51,7 @@ public class MotorolaHiddenMenuKeySequence {
* @return true if the input matches any pattern
*/
static boolean handleCharSequence(Context context, String input) {
- getInstance(context);
- if (!featureHiddenMenuEnabled) {
+ if (!getInstance(context).featureHiddenMenuEnabled) {
return false;
}
return handleKeySequence(context, input) || handleKeyPattern(context, input);
@@ -66,60 +70,81 @@ public class MotorolaHiddenMenuKeySequence {
return instance;
}
- private MotorolaHiddenMenuKeySequence(Context context) {
- featureHiddenMenuEnabled = MotorolaUtils.isSupportingHiddenMenu(context);
- // In case we do have a SPN from resource we need to match from service; otherwise we are
- // free to go
- if (featureHiddenMenuEnabled) {
-
- hiddenKeySequenceArray =
- context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence);
- hiddenKeySequenceIntentArray =
- context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents);
- hiddenKeyPatternArray =
- context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern);
- hiddenKeyPatternIntentArray =
- context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents);
-
- if (hiddenKeySequenceArray.length != hiddenKeySequenceIntentArray.length
- || hiddenKeyPatternArray.length != hiddenKeyPatternIntentArray.length
- || (hiddenKeySequenceArray.length == 0 && hiddenKeyPatternArray.length == 0)) {
- LogUtil.e(
- "MotorolaHiddenMenuKeySequence",
- "the key sequence array is not matching, turn off feature."
- + "key sequence: %d != %d, key pattern %d != %d",
- hiddenKeySequenceArray.length,
- hiddenKeySequenceIntentArray.length,
- hiddenKeyPatternArray.length,
- hiddenKeyPatternIntentArray.length);
- featureHiddenMenuEnabled = false;
- }
+ @VisibleForTesting
+ MotorolaHiddenMenuKeySequence(Context context) {
+ if (MotorolaUtils.isSupportingHiddenMenu(context)) {
+ Collections.addAll(
+ hiddenKeySequences,
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence));
+ Collections.addAll(
+ hiddenKeySequenceIntents,
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_sequence_intents));
+ Collections.addAll(
+ hiddenKeyPatterns,
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern));
+ Collections.addAll(
+ hiddenKeyPatternIntents,
+ context.getResources().getStringArray(R.array.motorola_hidden_menu_key_pattern_intents));
+ featureHiddenMenuEnabled = true;
+ }
+
+ if ("tracfone".equals(System.getProperty("ro.carrier"))) {
+ addHiddenKeySequence("#83865625#", "com.motorola.extensions.TFUnlock");
+ addHiddenKeySequence("#83782887#", "com.motorola.extensions.TFStatus");
+ featureHiddenMenuEnabled = true;
}
+
+ if (hiddenKeySequences.size() != hiddenKeySequenceIntents.size()
+ || hiddenKeyPatterns.size() != hiddenKeyPatternIntents.size()
+ || (hiddenKeySequences.isEmpty() && hiddenKeyPatterns.isEmpty())) {
+ LogUtil.e(
+ "MotorolaHiddenMenuKeySequence",
+ "the key sequence array is not matching, turn off feature."
+ + "key sequence: %d != %d, key pattern %d != %d",
+ hiddenKeySequences.size(),
+ hiddenKeySequenceIntents.size(),
+ hiddenKeyPatterns.size(),
+ hiddenKeyPatternIntents.size());
+ featureHiddenMenuEnabled = false;
+ }
+ }
+
+ private void addHiddenKeySequence(String keySequence, String intentAction) {
+ hiddenKeySequences.add(keySequence);
+ hiddenKeySequenceIntents.add(intentAction);
}
private static boolean handleKeyPattern(Context context, String input) {
+ MotorolaHiddenMenuKeySequence instance = getInstance(context);
+
int len = input.length();
- if (len <= 3 || hiddenKeyPatternArray == null || hiddenKeyPatternIntentArray == null) {
+ if (len <= 3
+ || instance.hiddenKeyPatterns == null
+ || instance.hiddenKeyPatternIntents == null) {
return false;
}
- for (int i = 0; i < hiddenKeyPatternArray.length; i++) {
- if ((Pattern.compile(hiddenKeyPatternArray[i])).matcher(input).matches()) {
- return sendIntent(context, input, hiddenKeyPatternIntentArray[i]);
+ for (int i = 0; i < instance.hiddenKeyPatterns.size(); i++) {
+ if (Pattern.matches(instance.hiddenKeyPatterns.get(i), input)) {
+ return sendIntent(context, input, instance.hiddenKeyPatternIntents.get(i));
}
}
return false;
}
private static boolean handleKeySequence(Context context, String input) {
+ MotorolaHiddenMenuKeySequence instance = getInstance(context);
+
int len = input.length();
- if (len <= 3 || hiddenKeySequenceArray == null || hiddenKeySequenceIntentArray == null) {
+ if (len <= 3
+ || instance.hiddenKeySequences == null
+ || instance.hiddenKeySequenceIntents == null) {
return false;
}
- for (int i = 0; i < hiddenKeySequenceArray.length; i++) {
- if (hiddenKeySequenceArray[i].equals(input)) {
- return sendIntent(context, input, hiddenKeySequenceIntentArray[i]);
+ for (int i = 0; i < instance.hiddenKeySequences.size(); i++) {
+ if (instance.hiddenKeySequences.get(i).equals(input)) {
+ return sendIntent(context, input, instance.hiddenKeySequenceIntents.get(i));
}
}
return false;
diff --git a/java/com/android/dialer/oem/MotorolaUtils.java b/java/com/android/dialer/oem/MotorolaUtils.java
index c1e2da256..1446a0219 100644
--- a/java/com/android/dialer/oem/MotorolaUtils.java
+++ b/java/com/android/dialer/oem/MotorolaUtils.java
@@ -43,7 +43,8 @@ public class MotorolaUtils {
// package is enabled.
@VisibleForTesting public static final String WIFI_CALL_PACKAGE_NAME = "com.motorola.sprintwfc";
// Thi is used to check if a Motorola device supports hidden menu feature.
- private static final String HIDDEN_MENU_FEATURE = "com.motorola.software.sprint.hidden_menu";
+ @VisibleForTesting
+ static final String HIDDEN_MENU_FEATURE = "com.motorola.software.sprint.hidden_menu";
private static boolean hasCheckedSprintWifiCall;
private static boolean supportSprintWifiCall;
diff --git a/java/com/android/dialer/oem/res/values-mcc310-mnc120/motorola_config.xml b/java/com/android/dialer/oem/res/values-mcc310-mnc120/motorola_config.xml
index c5cb0d1f7..417a4b845 100644
--- a/java/com/android/dialer/oem/res/values-mcc310-mnc120/motorola_config.xml
+++ b/java/com/android/dialer/oem/res/values-mcc310-mnc120/motorola_config.xml
@@ -17,4 +17,60 @@
<resources>
<bool name="motorola_sprint_hd_codec">true</bool>
+
+ <!-- Hidden menu configuration for Motorola. -->
+ <!-- This defines the specific key sequence that will be caught in the SpecialCharSequenceMgr
+ such as, ##OMADM# -->
+ <string-array name="motorola_hidden_menu_key_sequence">
+ <item>##66236#</item> <!--##OMADM#-->
+ <item>##2539#</item> <!--##AKEY#-->
+ <item>##786#</item> <!--##RTN#-->
+ <item>##72786#</item> <!--##SCRTN#-->
+ <item>##3282#</item> <!--##DATA#-->
+ <item>##33284#</item> <!--##DEBUG#-->
+ <item>##3424#</item> <!--##DIAG#-->
+ <item>##564#</item> <!--##LOG#-->
+ <item>##4567257#</item> <!--##GLMSCLR#-->
+ <item>##873283#</item> <!--##UPDATE#-->
+ <item>##6343#</item> <!--##MEID#-->
+ <item>##27263#</item> <!--##BRAND#-->
+ <item>##258#</item> <!--##BLV#-->
+ <item>##8422#</item> <!--##UICC#-->
+ <item>##4382#</item> <!--CMAS/WEA-->
+ </string-array>
+
+ <string name="motorola_hidden_menu_intent">com.motorola.intent.action.LAUNCH_HIDDEN_MENU</string>
+
+ <!-- This defines the intents that will be send out when the key sequence is matched, this must be
+ in the same order with he KeySequence array. -->
+ <string-array name="motorola_hidden_menu_key_sequence_intents">
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>com.motorola.android.intent.action.omadm.sprint.hfa</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ <item>@string/motorola_hidden_menu_intent</item>
+ </string-array>
+
+ <!-- This defines the specific key patterns that will be caught in the SpecialCharSequenceMgr
+ such as, ##[0-9]{3,7}# -->
+ <string-array name="motorola_hidden_menu_key_pattern">
+ <!--##MSL#, here MSL is 6 digits SPC code, ##OTKSL#, OTKSL is also digits code -->
+ <item>##[0-9]{6}#</item>
+ </string-array>
+
+ <!-- This defines the intents that will be send out when the key sequence is matched, this must be
+ in the same order with he KeyPattern array. -->
+ <string-array name="motorola_hidden_menu_key_pattern_intents">
+ <item>@string/motorola_hidden_menu_intent</item>
+ </string-array>
</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/oem/res/values/motorola_config.xml b/java/com/android/dialer/oem/res/values/motorola_config.xml
index ba451e715..fd9cee0a9 100644
--- a/java/com/android/dialer/oem/res/values/motorola_config.xml
+++ b/java/com/android/dialer/oem/res/values/motorola_config.xml
@@ -20,59 +20,26 @@
<bool name="motorola_sprint_hd_codec">false</bool>
<!-- Hidden menu configuration for Motorola. -->
- <!-- This defines the specific key seuquence that will be catched in the SpecialCharSequenceMgr
+ <!-- This defines the specific key sequence that will be caught in the SpecialCharSequenceMgr
such as, ##OMADM# -->
<string-array name="motorola_hidden_menu_key_sequence">
- <item>##66236#</item> <!--##OMADM#-->
- <item>##2539#</item> <!--##AKEY#-->
- <item>##786#</item> <!--##RTN#-->
- <item>##72786#</item> <!--##SCRTN#-->
- <item>##3282#</item> <!--##DATA#-->
- <item>##33284#</item> <!--##DEBUG#-->
- <item>##3424#</item> <!--##DIAG#-->
- <item>##564#</item> <!--##LOG#-->
- <item>##4567257#</item> <!--##GLMSCLR#-->
- <item>##873283#</item> <!--##UPDATE#-->
- <item>##6343#</item> <!--##MEID#-->
- <item>##27263#</item> <!--##BRAND#-->
- <item>##258#</item> <!--##BLV#-->
- <item>##8422#</item> <!--##UICC#-->
- <item>##4382#</item> <!--CMAS/WEA-->
</string-array>
- <string name="motorola_hidden_menu_intent">com.motorola.intent.action.LAUNCH_HIDDEN_MENU</string>
+ <string name="motorola_hidden_menu_intent"></string>
- <!-- This defines the intents that will be send out when the key quence is matched, this must be
+ <!-- This defines the intents that will be send out when the key sequence is matched, this must be
in the same order with he KeySequence array. -->
<string-array name="motorola_hidden_menu_key_sequence_intents">
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>com.motorola.android.intent.action.omadm.sprint.hfa</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
- <item>@string/motorola_hidden_menu_intent</item>
</string-array>
- <!-- This defines the specific key patterns that will be catched in the SpecialCharSequenceMgr
+ <!-- This defines the specific key patterns that will be caught in the SpecialCharSequenceMgr
such as, ##[0-9]{3,7}# -->
<string-array name="motorola_hidden_menu_key_pattern">
- <!--##MSL#, here MSL is 6 digits SPC code, ##OTKSL#, OTKSL is also digits code -->
- <item>##[0-9]{6}#</item>
</string-array>
- <!-- This defines the intents that will be send out when the key quence is matched, this must be
+ <!-- This defines the intents that will be send out when the key sequence is matched, this must be
in the same order with he KeyPattern array. -->
<string-array name="motorola_hidden_menu_key_pattern_intents">
- <item>@string/motorola_hidden_menu_intent</item>
</string-array>
<!-- This defines the provider names for cequint callerid applications
@@ -80,4 +47,4 @@
<string-array name="cequint_providers">
<item>com.cequint.ecid</item>
</string-array>
-</resources> \ No newline at end of file
+</resources>
diff --git a/java/com/android/dialer/phonelookup/PhoneLookup.java b/java/com/android/dialer/phonelookup/PhoneLookup.java
index 76ff98e7c..a7974ad10 100644
--- a/java/com/android/dialer/phonelookup/PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/PhoneLookup.java
@@ -84,14 +84,5 @@ public interface PhoneLookup<T> {
ListenableFuture<Void> onSuccessfulBulkUpdate();
@MainThread
- void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks);
-
- /**
- * Methods which may optionally be called as a result of a phone lookup's content observer firing.
- */
- interface ContentObserverCallbacks {
- @MainThread
- void markDirtyAndNotify(Context appContext);
- }
+ void registerContentObservers(Context appContext);
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
index 2271c7580..b6b02e135 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/DialerBlockedNumberPhoneLookup.java
@@ -22,6 +22,7 @@ import android.support.annotation.WorkerThread;
import android.util.ArraySet;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.calllog.observer.MarkDirtyObserver;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
@@ -51,13 +52,16 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
private final Context appContext;
private final ListeningExecutorService executorService;
+ private final MarkDirtyObserver markDirtyObserver;
@Inject
DialerBlockedNumberPhoneLookup(
@ApplicationContext Context appContext,
- @BackgroundExecutor ListeningExecutorService executorService) {
+ @BackgroundExecutor ListeningExecutorService executorService,
+ MarkDirtyObserver markDirtyObserver) {
this.appContext = appContext;
this.executorService = executorService;
+ this.markDirtyObserver = markDirtyObserver;
}
@Override
@@ -165,13 +169,12 @@ public final class DialerBlockedNumberPhoneLookup implements PhoneLookup<DialerB
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
appContext
.getContentResolver()
.registerContentObserver(
FilteredNumber.CONTENT_URI,
true, // FilteredNumberProvider notifies on the item
- new MarkDirtyObserver(appContext, contentObserverCallbacks));
+ markDirtyObserver);
}
}
diff --git a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
index e0ff995e7..d791e9b9e 100644
--- a/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/blockednumber/SystemBlockedNumberPhoneLookup.java
@@ -27,6 +27,7 @@ import android.support.annotation.WorkerThread;
import android.util.ArraySet;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.blocking.FilteredNumberCompat;
+import com.android.dialer.calllog.observer.MarkDirtyObserver;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
@@ -54,13 +55,16 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
private final Context appContext;
private final ListeningExecutorService executorService;
+ private final MarkDirtyObserver markDirtyObserver;
@Inject
SystemBlockedNumberPhoneLookup(
@ApplicationContext Context appContext,
- @BackgroundExecutor ListeningExecutorService executorService) {
+ @BackgroundExecutor ListeningExecutorService executorService,
+ MarkDirtyObserver markDirtyObserver) {
this.appContext = appContext;
this.executorService = executorService;
+ this.markDirtyObserver = markDirtyObserver;
}
@Override
@@ -166,8 +170,7 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
if (VERSION.SDK_INT < VERSION_CODES.N) {
return;
}
@@ -176,6 +179,6 @@ public class SystemBlockedNumberPhoneLookup implements PhoneLookup<SystemBlocked
.registerContentObserver(
BlockedNumbers.CONTENT_URI,
true, // BlockedNumbers notifies on the item
- new MarkDirtyObserver(appContext, contentObserverCallbacks));
+ markDirtyObserver);
}
}
diff --git a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
index b77a86ca0..abe18f7dc 100644
--- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java
@@ -179,10 +179,9 @@ public final class CompositePhoneLookup implements PhoneLookup<PhoneLookupInfo>
@Override
@MainThread
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
for (PhoneLookup phoneLookup : phoneLookups) {
- phoneLookup.registerContentObservers(appContext, contentObserverCallbacks);
+ phoneLookup.registerContentObservers(appContext);
}
}
}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
index e051f473c..8db308892 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2LocalPhoneLookup.java
@@ -619,8 +619,7 @@ public final class Cp2LocalPhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
// Do nothing since CP2 changes are too noisy.
}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
index cc4fbf19f..7efe039eb 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2RemotePhoneLookup.java
@@ -237,8 +237,7 @@ public final class Cp2RemotePhoneLookup implements PhoneLookup<Cp2Info> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ public void registerContentObservers(Context appContext) {
// No content observer needed for remote contacts
}
}
diff --git a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
index 9f0b5cf52..7661a15da 100644
--- a/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/spam/SpamPhoneLookup.java
@@ -152,8 +152,8 @@ public final class SpamPhoneLookup implements PhoneLookup<SpamInfo> {
}
@Override
- public void registerContentObservers(
- Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
- // No content observer needed for spam info
+ public void registerContentObservers(Context appContext) {
+ // No content observer can be registered as Spam is not based on a content provider.
+ // Each Spam implementation should be responsible for notifying any data changes.
}
}
diff --git a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
index 04226552d..e99533a40 100644
--- a/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
+++ b/java/com/android/dialer/phonenumbercache/ContactInfoHelper.java
@@ -15,7 +15,6 @@
package com.android.dialer.phonenumbercache;
import android.annotation.TargetApi;
-import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
@@ -299,12 +298,12 @@ public class ContactInfoHelper {
VERSION.SDK_INT >= VERSION_CODES.N
? Directory.ENTERPRISE_CONTENT_URI
: Directory.CONTENT_URI;
- ContentResolver cr = context.getContentResolver();
- Cursor cursor = cr.query(uri, new String[] {Directory._ID}, null, null, null);
- int idIndex = cursor.getColumnIndex(Directory._ID);
+ Cursor cursor =
+ context.getContentResolver().query(uri, new String[] {Directory._ID}, null, null, null);
if (cursor == null) {
return remoteDirectories;
}
+ int idIndex = cursor.getColumnIndex(Directory._ID);
try {
while (cursor.moveToNext()) {
long directoryId = cursor.getLong(idIndex);
diff --git a/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java b/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java
index 39c1187a4..dbe11dd96 100644
--- a/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java
@@ -31,7 +31,12 @@ import com.google.auto.value.AutoValue;
import java.util.ArrayList;
import java.util.List;
-/** {@link CursorLoader} to load the list of all directories (local and remote). */
+/**
+ * {@link CursorLoader} to load information about all directories (local and remote).
+ *
+ * <p>Information about a directory includes its ID, display name, etc, but doesn't include the
+ * contacts in it.
+ */
public final class DirectoriesCursorLoader extends CursorLoader {
public static final String[] PROJECTION = {
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java b/java/com/android/dialer/searchfragment/directories/DirectoryContactViewHolder.java
index 4be96fe58..ff321fc75 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/directories/DirectoryContactViewHolder.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.searchfragment.remote;
+package com.android.dialer.searchfragment.directories;
import android.content.Context;
import android.content.res.Resources;
@@ -40,8 +40,8 @@ import com.android.dialer.searchfragment.common.QueryBoldingUtil;
import com.android.dialer.searchfragment.common.R;
import com.android.dialer.searchfragment.common.SearchCursor;
-/** ViewHolder for a nearby place row. */
-public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
+/** ViewHolder for a directory contact row. */
+public final class DirectoryContactViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener {
private final Context context;
@@ -52,7 +52,7 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
private String number;
- public RemoteContactViewHolder(View view) {
+ public DirectoryContactViewHolder(View view) {
super(view);
view.setOnClickListener(this);
photo = view.findViewById(R.id.photo);
@@ -63,8 +63,8 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
}
/**
- * Binds the ViewHolder with a cursor from {@link RemoteContactsCursorLoader} with the data found
- * at the cursors current position.
+ * Binds the ViewHolder with a cursor from {@link DirectoryContactsCursorLoader} with the data
+ * found at the cursors current position.
*/
public void bind(SearchCursor cursor, String query) {
number = cursor.getString(Projections.PHONE_NUMBER);
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java b/java/com/android/dialer/searchfragment/directories/DirectoryContactsCursor.java
index 653c67041..bf0bdc057 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
+++ b/java/com/android/dialer/searchfragment/directories/DirectoryContactsCursor.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.searchfragment.remote;
+package com.android.dialer.searchfragment.directories;
import android.content.Context;
import android.database.Cursor;
@@ -25,23 +25,22 @@ import android.support.annotation.VisibleForTesting;
import com.android.contacts.common.compat.DirectoryCompat;
import com.android.dialer.common.Assert;
import com.android.dialer.searchfragment.common.SearchCursor;
-import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader;
import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
- * {@link MergeCursor} used for combining remote directory cursors into one cursor.
+ * {@link MergeCursor} used for combining directory cursors into one cursor.
*
- * <p>Usually a device with multiple Google accounts will have multiple remote directories returned
- * by {@link DirectoriesCursorLoader}, each represented as a {@link Directory}.
+ * <p>Usually a device with multiple Google accounts will have multiple directories returned by
+ * {@link DirectoriesCursorLoader}, each represented as a {@link Directory}.
*
* <p>This cursor merges them together with a header at the start of each cursor/list using {@link
* Directory#getDisplayName()} as the header text.
*/
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
-public final class RemoteContactsCursor extends MergeCursor implements SearchCursor {
+public final class DirectoryContactsCursor extends MergeCursor implements SearchCursor {
/**
* {@link SearchCursor#HEADER_PROJECTION} with {@link #COLUMN_DIRECTORY_ID} appended on the end.
@@ -59,7 +58,7 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur
*/
@Nullable
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- public static RemoteContactsCursor newInstance(
+ public static DirectoryContactsCursor newInstance(
Context context, Cursor[] cursors, List<Directory> directories) {
Assert.checkArgument(
cursors.length == directories.size(),
@@ -68,12 +67,12 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur
cursors.length);
Cursor[] cursorsWithHeaders = insertHeaders(context, cursors, directories);
if (cursorsWithHeaders.length > 0) {
- return new RemoteContactsCursor(cursorsWithHeaders);
+ return new DirectoryContactsCursor(cursorsWithHeaders);
}
return null;
}
- private RemoteContactsCursor(Cursor[] cursors) {
+ private DirectoryContactsCursor(Cursor[] cursors) {
super(cursors);
}
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java b/java/com/android/dialer/searchfragment/directories/DirectoryContactsCursorLoader.java
index cf495e49c..fc36f59bb 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/directories/DirectoryContactsCursorLoader.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.dialer.searchfragment.remote;
+package com.android.dialer.searchfragment.directories;
import android.content.Context;
import android.content.CursorLoader;
@@ -37,10 +37,10 @@ import java.util.List;
* Cursor loader to load extended contacts on device.
*
* <p>This loader performs several database queries in serial and merges the resulting cursors
- * together into {@link RemoteContactsCursor}. If there are no results, the loader will return a
+ * together into {@link DirectoryContactsCursor}. If there are no results, the loader will return a
* null cursor.
*/
-public final class RemoteContactsCursorLoader extends CursorLoader {
+public final class DirectoryContactsCursorLoader extends CursorLoader {
private static final Uri ENTERPRISE_CONTENT_FILTER_URI =
Uri.withAppendedPath(Phone.CONTENT_URI, "filter_enterprise");
@@ -53,7 +53,7 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
private final List<Directory> directories;
private final Cursor[] cursors;
- public RemoteContactsCursorLoader(Context context, String query, List<Directory> directories) {
+ public DirectoryContactsCursorLoader(Context context, String query, List<Directory> directories) {
super(
context,
null,
@@ -71,14 +71,14 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
for (int i = 0; i < directories.size(); i++) {
Directory directory = directories.get(i);
- // Filter out local directories
+ // Only load contacts in the enterprise directory & remote directories.
if (!DirectoryCompat.isRemoteDirectoryId(directory.getId())
&& !DirectoryCompat.isEnterpriseDirectoryId(directory.getId())) {
cursors[i] = null;
continue;
}
- // Filter out invisible directories
+ // Filter out invisible directories.
if (DirectoryCompat.isInvisibleDirectory(directory.getId())) {
cursors[i] = null;
continue;
@@ -98,7 +98,7 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
// number. In this case just hide the row entirely. See a bug.
cursors[i] = createMatrixCursorFilteringNullNumbers(cursor);
}
- return RemoteContactsCursor.newInstance(getContext(), cursors, directories);
+ return DirectoryContactsCursor.newInstance(getContext(), cursors, directories);
}
private MatrixCursor createMatrixCursorFilteringNullNumbers(Cursor cursor) {
diff --git a/java/com/android/dialer/searchfragment/remote/res/values/strings.xml b/java/com/android/dialer/searchfragment/directories/res/values/strings.xml
index beabba135..beabba135 100644
--- a/java/com/android/dialer/searchfragment/remote/res/values/strings.xml
+++ b/java/com/android/dialer/searchfragment/directories/res/values/strings.xml
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index 2d45457d2..aff946206 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -65,9 +65,9 @@ import com.android.dialer.searchfragment.common.SearchCursor;
import com.android.dialer.searchfragment.cp2.SearchContactsCursorLoader;
import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader;
import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory;
+import com.android.dialer.searchfragment.directories.DirectoryContactsCursorLoader;
import com.android.dialer.searchfragment.list.SearchActionViewHolder.Action;
import com.android.dialer.searchfragment.nearbyplaces.NearbyPlacesCursorLoader;
-import com.android.dialer.searchfragment.remote.RemoteContactsCursorLoader;
import com.android.dialer.storage.StorageComponent;
import com.android.dialer.util.CallUtil;
import com.android.dialer.util.DialerUtils;
@@ -103,8 +103,11 @@ public final class NewSearchFragment extends Fragment
private static final int CONTACTS_LOADER_ID = 0;
private static final int NEARBY_PLACES_LOADER_ID = 1;
- private static final int REMOTE_DIRECTORIES_LOADER_ID = 2;
- private static final int REMOTE_CONTACTS_LOADER_ID = 3;
+
+ // ID for the loader that loads info about all directories (local & remote).
+ private static final int DIRECTORIES_LOADER_ID = 2;
+
+ private static final int DIRECTORY_CONTACTS_LOADER_ID = 3;
private static final String KEY_QUERY = "key_query";
private static final String KEY_CALL_INITIATION_TYPE = "key_call_initiation_type";
@@ -117,15 +120,17 @@ public final class NewSearchFragment extends Fragment
// for actions to add contact or send sms.
private String rawNumber;
private CallInitiationType.Type callInitiationType = CallInitiationType.Type.UNKNOWN_INITIATION;
- private boolean remoteDirectoriesDisabledForTesting;
+ private boolean directoriesDisabledForTesting;
+ // Information about all local & remote directories (including ID, display name, etc, but not
+ // the contacts in them).
private final List<Directory> directories = new ArrayList<>();
private final Runnable loaderCp2ContactsRunnable =
() -> getLoaderManager().restartLoader(CONTACTS_LOADER_ID, null, this);
private final Runnable loadNearbyPlacesRunnable =
() -> getLoaderManager().restartLoader(NEARBY_PLACES_LOADER_ID, null, this);
- private final Runnable loadRemoteContactsRunnable =
- () -> getLoaderManager().restartLoader(REMOTE_CONTACTS_LOADER_ID, null, this);
+ private final Runnable loadDirectoryContactsRunnable =
+ () -> getLoaderManager().restartLoader(DIRECTORY_CONTACTS_LOADER_ID, null, this);
private final Runnable capabilitiesUpdatedRunnable = () -> adapter.notifyDataSetChanged();
private Runnable updatePositionRunnable;
@@ -184,7 +189,7 @@ public final class NewSearchFragment extends Fragment
private void initLoaders() {
getLoaderManager().initLoader(CONTACTS_LOADER_ID, null, this);
- loadRemoteDirectoriesCursor();
+ loadDirectoriesCursor();
}
@Override
@@ -201,10 +206,10 @@ public final class NewSearchFragment extends Fragment
directoryIds.add(directory.getId());
}
return new NearbyPlacesCursorLoader(getContext(), query, directoryIds);
- } else if (id == REMOTE_DIRECTORIES_LOADER_ID) {
+ } else if (id == DIRECTORIES_LOADER_ID) {
return new DirectoriesCursorLoader(getContext());
- } else if (id == REMOTE_CONTACTS_LOADER_ID) {
- return new RemoteContactsCursorLoader(getContext(), query, directories);
+ } else if (id == DIRECTORY_CONTACTS_LOADER_ID) {
+ return new DirectoryContactsCursorLoader(getContext(), query, directories);
} else {
throw new IllegalStateException("Invalid loader id: " + id);
}
@@ -225,14 +230,14 @@ public final class NewSearchFragment extends Fragment
} else if (loader instanceof NearbyPlacesCursorLoader) {
adapter.setNearbyPlacesCursor((SearchCursor) cursor);
- } else if (loader instanceof RemoteContactsCursorLoader) {
- adapter.setRemoteContactsCursor((SearchCursor) cursor);
+ } else if (loader instanceof DirectoryContactsCursorLoader) {
+ adapter.setDirectoryContactsCursor((SearchCursor) cursor);
} else if (loader instanceof DirectoriesCursorLoader) {
directories.clear();
directories.addAll(DirectoriesCursorLoader.toDirectories(cursor));
loadNearbyPlacesCursor();
- loadRemoteContactsCursors();
+ loadDirectoryContactsCursors();
} else {
throw new IllegalStateException("Invalid loader: " + loader);
@@ -246,8 +251,8 @@ public final class NewSearchFragment extends Fragment
adapter.setContactsCursor(null);
} else if (loader instanceof NearbyPlacesCursorLoader) {
adapter.setNearbyPlacesCursor(null);
- } else if (loader instanceof RemoteContactsCursorLoader) {
- adapter.setRemoteContactsCursor(null);
+ } else if (loader instanceof DirectoryContactsCursorLoader) {
+ adapter.setDirectoryContactsCursor(null);
}
}
@@ -264,7 +269,7 @@ public final class NewSearchFragment extends Fragment
adapter.setZeroSuggestVisible(isRegularSearch());
loadCp2ContactsCursor();
loadNearbyPlacesCursor();
- loadRemoteContactsCursors();
+ loadDirectoryContactsCursors();
}
}
@@ -306,7 +311,7 @@ public final class NewSearchFragment extends Fragment
super.onDestroy();
ThreadUtil.getUiThreadHandler().removeCallbacks(loaderCp2ContactsRunnable);
ThreadUtil.getUiThreadHandler().removeCallbacks(loadNearbyPlacesRunnable);
- ThreadUtil.getUiThreadHandler().removeCallbacks(loadRemoteContactsRunnable);
+ ThreadUtil.getUiThreadHandler().removeCallbacks(loadDirectoryContactsRunnable);
ThreadUtil.getUiThreadHandler().removeCallbacks(capabilitiesUpdatedRunnable);
}
@@ -342,23 +347,27 @@ public final class NewSearchFragment extends Fragment
}
}
- // Loads remote directories.
- private void loadRemoteDirectoriesCursor() {
- if (!remoteDirectoriesDisabledForTesting) {
- getLoaderManager().initLoader(REMOTE_DIRECTORIES_LOADER_ID, null, this);
+ /** Loads info about all directories (local & remote). */
+ private void loadDirectoriesCursor() {
+ if (!directoriesDisabledForTesting) {
+ getLoaderManager().initLoader(DIRECTORIES_LOADER_ID, null, this);
}
}
- // Should not be called before remote directories have finished loading.
- private void loadRemoteContactsCursors() {
- if (remoteDirectoriesDisabledForTesting) {
+ /**
+ * Loads contacts stored in directories.
+ *
+ * <p>Should not be called before finishing loading info about all directories (local & remote).
+ */
+ private void loadDirectoryContactsCursors() {
+ if (directoriesDisabledForTesting) {
return;
}
// Cancel existing load if one exists.
- ThreadUtil.getUiThreadHandler().removeCallbacks(loadRemoteContactsRunnable);
+ ThreadUtil.getUiThreadHandler().removeCallbacks(loadDirectoryContactsRunnable);
ThreadUtil.getUiThreadHandler()
- .postDelayed(loadRemoteContactsRunnable, NETWORK_SEARCH_DELAY_MILLIS);
+ .postDelayed(loadDirectoryContactsRunnable, NETWORK_SEARCH_DELAY_MILLIS);
}
private void loadCp2ContactsCursor() {
@@ -368,7 +377,11 @@ public final class NewSearchFragment extends Fragment
.postDelayed(loaderCp2ContactsRunnable, NETWORK_SEARCH_DELAY_MILLIS);
}
- // Should not be called before remote directories (not contacts) have finished loading.
+ /**
+ * Loads nearby places.
+ *
+ * <p>Should not be called before finishing loading info about all directories (local and remote).
+ */
private void loadNearbyPlacesCursor() {
if (!PermissionsUtil.hasLocationPermissions(getContext())
&& !StorageComponent.get(getContext())
@@ -443,8 +456,8 @@ public final class NewSearchFragment extends Fragment
// being untestable while it can query multiple datasources. This is a temporary fix.
// TODO(a bug): Remove this method and test this fragment with multiple data sources
@VisibleForTesting
- public void setRemoteDirectoriesDisabled(boolean disabled) {
- remoteDirectoriesDisabledForTesting = disabled;
+ public void setDirectoriesDisabled(boolean disabled) {
+ directoriesDisabledForTesting = disabled;
}
/**
diff --git a/java/com/android/dialer/searchfragment/list/SearchAdapter.java b/java/com/android/dialer/searchfragment/list/SearchAdapter.java
index 1681097bf..462426943 100644
--- a/java/com/android/dialer/searchfragment/list/SearchAdapter.java
+++ b/java/com/android/dialer/searchfragment/list/SearchAdapter.java
@@ -31,9 +31,9 @@ import com.android.dialer.common.Assert;
import com.android.dialer.searchfragment.common.RowClickListener;
import com.android.dialer.searchfragment.common.SearchCursor;
import com.android.dialer.searchfragment.cp2.SearchContactViewHolder;
+import com.android.dialer.searchfragment.directories.DirectoryContactViewHolder;
import com.android.dialer.searchfragment.list.SearchCursorManager.RowType;
import com.android.dialer.searchfragment.nearbyplaces.NearbyPlaceViewHolder;
-import com.android.dialer.searchfragment.remote.RemoteContactViewHolder;
import java.util.List;
/** RecyclerView adapter for {@link NewSearchFragment}. */
@@ -77,7 +77,7 @@ public final class SearchAdapter extends RecyclerView.Adapter<ViewHolder> {
return new HeaderViewHolder(
LayoutInflater.from(context).inflate(R.layout.header_layout, root, false));
case RowType.DIRECTORY_ROW:
- return new RemoteContactViewHolder(
+ return new DirectoryContactViewHolder(
LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false));
case RowType.SEARCH_ACTION:
return new SearchActionViewHolder(
@@ -104,8 +104,8 @@ public final class SearchAdapter extends RecyclerView.Adapter<ViewHolder> {
((SearchContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query);
} else if (holder instanceof NearbyPlaceViewHolder) {
((NearbyPlaceViewHolder) holder).bind(searchCursorManager.getCursor(position), query);
- } else if (holder instanceof RemoteContactViewHolder) {
- ((RemoteContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query);
+ } else if (holder instanceof DirectoryContactViewHolder) {
+ ((DirectoryContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query);
} else if (holder instanceof HeaderViewHolder) {
String header =
searchCursorManager.getCursor(position).getString(SearchCursor.HEADER_TEXT_POSITION);
@@ -200,8 +200,8 @@ public final class SearchAdapter extends RecyclerView.Adapter<ViewHolder> {
}
}
- public void setRemoteContactsCursor(SearchCursor remoteContactsCursor) {
- if (searchCursorManager.setCorpDirectoryCursor(remoteContactsCursor)) {
+ void setDirectoryContactsCursor(SearchCursor directoryContactsCursor) {
+ if (searchCursorManager.setCorpDirectoryCursor(directoryContactsCursor)) {
notifyDataSetChanged();
}
}
diff --git a/java/com/android/dialer/searchfragment/remote/AndroidManifest.xml b/java/com/android/dialer/searchfragment/remote/AndroidManifest.xml
deleted file mode 100644
index e52f5319e..000000000
--- a/java/com/android/dialer/searchfragment/remote/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<!--
- ~ 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
- -->
-<manifest package="com.android.dialer.searchfragment.remote"/> \ No newline at end of file
diff --git a/java/com/android/dialer/simulator/impl/RttChatBot.java b/java/com/android/dialer/simulator/impl/RttChatBot.java
index 9c2989a07..b2860e387 100644
--- a/java/com/android/dialer/simulator/impl/RttChatBot.java
+++ b/java/com/android/dialer/simulator/impl/RttChatBot.java
@@ -16,6 +16,7 @@
package com.android.dialer.simulator.impl;
+import android.annotation.TargetApi;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -30,6 +31,7 @@ import java.util.List;
import java.util.Random;
/** Chat bot to generate remote RTT chat messages. */
+@TargetApi(28)
public class RttChatBot {
interface Callback {
@@ -95,7 +97,6 @@ public class RttChatBot {
break;
case SEND_MESSAGE:
String message = (String) msg.obj;
- LogUtil.w("test", "type: %s, to stream: %s", message, rttTextStream);
try {
rttTextStream.write(message);
} catch (IOException e) {
diff --git a/java/com/android/dialer/simulator/impl/SimulatorConnection.java b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
index c832a5051..3aa3296ea 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorConnection.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorConnection.java
@@ -16,9 +16,12 @@
package com.android.dialer.simulator.impl;
+import android.annotation.TargetApi;
import android.content.Context;
import android.support.annotation.NonNull;
+import android.support.v4.os.BuildCompat;
import android.telecom.Connection;
+import android.telecom.Connection.RttTextStream;
import android.telecom.ConnectionRequest;
import android.telecom.VideoProfile;
import com.android.dialer.common.Assert;
@@ -31,11 +34,14 @@ import java.util.ArrayList;
import java.util.List;
/** Represents a single phone call on the device. */
+@TargetApi(28)
public final class SimulatorConnection extends Connection {
private final List<Listener> listeners = new ArrayList<>();
private final List<Event> events = new ArrayList<>();
private final SimulatorConnectionsBank simulatorConnectionsBank;
private int currentState = STATE_NEW;
+ private RttTextStream rttTextStream;
+ private RttChatBot rttChatBot;
SimulatorConnection(@NonNull Context context, @NonNull ConnectionRequest request) {
Assert.isNotNull(context);
@@ -54,6 +60,9 @@ public final class SimulatorConnection extends Connection {
getConnectionCapabilities() | CAPABILITY_SEPARATE_FROM_CONFERENCE);
}
}
+ if (BuildCompat.isAtLeastP()) {
+ rttTextStream = request.getRttTextStream();
+ }
setVideoProvider(new SimulatorVideoProvider(context, this));
simulatorConnectionsBank = SimulatorComponent.get(context).getSimulatorConnectionsBank();
}
@@ -66,6 +75,10 @@ public final class SimulatorConnection extends Connection {
listeners.remove(Assert.isNotNull(listener));
}
+ RttTextStream getRttTextStream() {
+ return rttTextStream;
+ }
+
@NonNull
public List<Event> getEvents() {
return events;
@@ -101,6 +114,11 @@ public final class SimulatorConnection extends Connection {
LogUtil.enterBlock("SimulatorConnection.onDisconnect");
simulatorConnectionsBank.remove(this);
onEvent(new Event(Event.DISCONNECT));
+ rttTextStream = null;
+ if (rttChatBot != null) {
+ rttChatBot.stop();
+ rttChatBot = null;
+ }
}
@Override
@@ -124,12 +142,21 @@ public final class SimulatorConnection extends Connection {
@Override
public void onStartRtt(@NonNull RttTextStream rttTextStream) {
LogUtil.enterBlock("SimulatorConnection.onStartRtt");
+ if (this.rttTextStream != null || rttChatBot != null) {
+ LogUtil.e("SimulatorConnection.onStartRtt", "rttTextStream or rttChatBot is not null!");
+ }
+ this.rttTextStream = rttTextStream;
+ rttChatBot = new RttChatBot(rttTextStream);
+ rttChatBot.start();
onEvent(new Event(Event.START_RTT));
}
@Override
public void onStopRtt() {
LogUtil.enterBlock("SimulatorConnection.onStopRtt");
+ rttChatBot.stop();
+ rttChatBot = null;
+ rttTextStream = null;
onEvent(new Event(Event.STOP_RTT));
}
@@ -159,6 +186,4 @@ public final class SimulatorConnection extends Connection {
public interface Listener {
void onEvent(@NonNull SimulatorConnection connection, @NonNull Event event);
}
-
-
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorImpl.java b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
index 24f34102e..c8b8af92e 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorImpl.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorImpl.java
@@ -19,6 +19,7 @@ package com.android.dialer.simulator.impl;
import android.support.v7.app.AppCompatActivity;
import android.view.ActionProvider;
import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.buildtype.BuildType.Type;
import com.android.dialer.common.LogUtil;
import com.android.dialer.simulator.Simulator;
import javax.inject.Inject;
@@ -33,7 +34,7 @@ final class SimulatorImpl implements Simulator {
@Override
public boolean shouldShow() {
- return BuildType.get() == BuildType.BUGFOOD || LogUtil.isDebugEnabled();
+ return BuildType.get() == Type.BUGFOOD || LogUtil.isDebugEnabled();
}
@Override
diff --git a/java/com/android/dialer/simulator/impl/SimulatorRttCall.java b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java
index 7b0066719..352b9e4ef 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorRttCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorRttCall.java
@@ -34,6 +34,7 @@ final class SimulatorRttCall
@NonNull private final Context context;
@Nullable private String connectionTag;
+ private RttChatBot rttChatBot;
static ActionProvider getActionProvider(@NonNull Context context) {
return new SimulatorSubMenu(context)
@@ -112,24 +113,29 @@ final class SimulatorRttCall
switch (event.type) {
case Event.NONE:
throw Assert.createIllegalStateFailException();
- case Event.ANSWER:
- connection.setActive();
- break;
case Event.REJECT:
connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
break;
case Event.HOLD:
connection.setOnHold();
break;
+ case Event.ANSWER:
case Event.UNHOLD:
connection.setActive();
break;
case Event.DISCONNECT:
+ rttChatBot.stop();
connection.setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
break;
case Event.SESSION_MODIFY_REQUEST:
ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
break;
+ case Event.STATE_CHANGE:
+ if (Connection.stateToString(Connection.STATE_ACTIVE).equals(event.data2)) {
+ rttChatBot = new RttChatBot(connection.getRttTextStream());
+ rttChatBot.start();
+ }
+ break;
default:
LogUtil.i("SimulatorRttCall.onEvent", "unexpected event: " + event.type);
break;
diff --git a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
index d51e06816..c56afb21f 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
@@ -115,7 +115,7 @@ public class SimulatorSimCallManager {
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
callType == CALL_TYPE_VIDEO
? getVideoProviderHandle(context)
- : getSystemPhoneAccountHandle(context));
+ : getSimCallManagerHandle(context));
if (callType == CALL_TYPE_RTT) {
outgoingCallExtras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
}
diff --git a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
index d4c7ee458..e59cddd51 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorVoiceCall.java
@@ -22,6 +22,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.telecom.Connection;
+import android.telecom.Connection.RttModifyStatus;
import android.telecom.DisconnectCause;
import android.view.ActionProvider;
import com.android.dialer.common.Assert;
@@ -223,15 +224,13 @@ final class SimulatorVoiceCall
switch (event.type) {
case Event.NONE:
throw Assert.createIllegalStateFailException();
- case Event.ANSWER:
- connection.setActive();
- break;
case Event.REJECT:
connection.setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
break;
case Event.HOLD:
connection.setOnHold();
break;
+ case Event.ANSWER:
case Event.UNHOLD:
connection.setActive();
break;
@@ -244,6 +243,15 @@ final class SimulatorVoiceCall
case Event.SESSION_MODIFY_REQUEST:
ThreadUtil.postDelayedOnUiThread(() -> connection.handleSessionModifyRequest(event), 2000);
break;
+ case Event.START_RTT:
+ // TODO(wangqi): Add random accept/decline.
+ boolean accept = true;
+ if (accept) {
+ connection.sendRttInitiationSuccess();
+ } else {
+ connection.sendRttInitiationFailure(RttModifyStatus.SESSION_MODIFY_REQUEST_FAIL);
+ }
+ break;
default:
LogUtil.i("SimulatorVoiceCall.onEvent", "unexpected event: " + event.type);
break;
diff --git a/java/com/android/dialer/strictmode/StrictModeUtils.java b/java/com/android/dialer/strictmode/StrictModeUtils.java
index c07138f81..27f8142d8 100644
--- a/java/com/android/dialer/strictmode/StrictModeUtils.java
+++ b/java/com/android/dialer/strictmode/StrictModeUtils.java
@@ -25,6 +25,7 @@ import android.preference.PreferenceManager;
import android.support.annotation.AnyThread;
import android.support.v4.os.UserManagerCompat;
import com.android.dialer.buildtype.BuildType;
+import com.android.dialer.buildtype.BuildType.Type;
import com.android.dialer.function.Supplier;
import com.android.dialer.storage.StorageComponent;
@@ -86,7 +87,7 @@ public final class StrictModeUtils {
}
public static boolean isStrictModeAllowed() {
- return BuildType.get() == BuildType.BUGFOOD;
+ return BuildType.get() == Type.BUGFOOD;
}
private static boolean onMainThread() {
diff --git a/java/com/android/dialer/theme/res/values/dimens.xml b/java/com/android/dialer/theme/res/values/dimens.xml
index e7c947782..88b8a0423 100644
--- a/java/com/android/dialer/theme/res/values/dimens.xml
+++ b/java/com/android/dialer/theme/res/values/dimens.xml
@@ -22,7 +22,7 @@
<dimen name="call_log_primary_text_size">16sp</dimen>
<dimen name="call_log_detail_text_size">12sp</dimen>
<dimen name="call_log_day_group_heading_size">14sp</dimen>
- <dimen name="call_log_voicemail_transcription_text_size">16sp</dimen>
+ <dimen name="call_log_voicemail_transcription_text_size">14sp</dimen>
<!-- Height of the call log actions section for each call log entry -->
<dimen name="call_log_action_height">48dp</dimen>
<dimen name="call_log_day_group_padding_top">15dp</dimen>
diff --git a/java/com/android/dialer/theme/res/values/styles.xml b/java/com/android/dialer/theme/res/values/styles.xml
index ac94d0687..d65d2af9c 100644
--- a/java/com/android/dialer/theme/res/values/styles.xml
+++ b/java/com/android/dialer/theme/res/values/styles.xml
@@ -45,7 +45,7 @@
</style>
<style name="DialerButtonTextStyle" parent="@android:style/TextAppearance.Material.Widget.Button">
- <item name="android:textColor">#fff</item>
+ <item name="android:textColor">@color/dialer_primary_text_color_white</item>
</style>
<style name="DialerActionBarBaseStyle"
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
index 8b6fcbc07..9296d04e7 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailFragment.java
@@ -24,18 +24,16 @@ import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.dialer.calllog.CallLogComponent;
-import com.android.dialer.calllog.CallLogFramework;
-import com.android.dialer.calllog.CallLogFramework.CallLogUi;
-import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker;
+import com.android.dialer.calllog.RefreshAnnotatedCallLogReceiver;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.common.concurrent.UiListener;
import com.android.dialer.glidephotomanager.GlidePhotoManagerComponent;
import com.android.dialer.voicemail.listui.error.VoicemailStatus;
@@ -48,24 +46,11 @@ import java.util.List;
// TODO(uabdullah): Register content observer for VoicemailContract.Status.CONTENT_URI in onStart
/** Fragment for Dialer Voicemail Tab. */
-public final class NewVoicemailFragment extends Fragment
- implements LoaderCallbacks<Cursor>, CallLogUi {
-
- /*
- * This is a reasonable time that it might take between related call log writes, that also
- * shouldn't slow down single-writes too much. For example, when populating the database using
- * the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120
- * call log entries.
- */
- private static final long WAIT_MILLIS = 100L;
-
- private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
- private UiListener<Void> refreshAnnotatedCallLogListener;
- @Nullable private Runnable refreshAnnotatedCallLogRunnable;
-
- private UiListener<ImmutableList<VoicemailStatus>> queryVoicemailStatusTableListener;
+public final class NewVoicemailFragment extends Fragment implements LoaderCallbacks<Cursor> {
private RecyclerView recyclerView;
+ private RefreshAnnotatedCallLogReceiver refreshAnnotatedCallLogReceiver;
+ private UiListener<ImmutableList<VoicemailStatus>> queryVoicemailStatusTableListener;
public NewVoicemailFragment() {
LogUtil.enterBlock("NewVoicemailFragment.NewVoicemailFragment");
@@ -77,23 +62,12 @@ public final class NewVoicemailFragment extends Fragment
LogUtil.enterBlock("NewVoicemailFragment.onActivityCreated");
- CallLogComponent component = CallLogComponent.get(getContext());
- CallLogFramework callLogFramework = component.callLogFramework();
- callLogFramework.attachUi(this);
-
- // TODO(zachh): Use support fragment manager and add support for them in executors library.
- refreshAnnotatedCallLogListener =
- DialerExecutorComponent.get(getContext())
- .createUiListener(
- getActivity().getFragmentManager(), "NewVoicemailFragment.refreshAnnotatedCallLog");
-
+ refreshAnnotatedCallLogReceiver = new RefreshAnnotatedCallLogReceiver(getContext());
queryVoicemailStatusTableListener =
DialerExecutorComponent.get(getContext())
.createUiListener(
getActivity().getFragmentManager(),
"NewVoicemailFragment.queryVoicemailStatusTable");
-
- refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker();
}
@Override
@@ -106,29 +80,69 @@ public final class NewVoicemailFragment extends Fragment
public void onResume() {
super.onResume();
- LogUtil.enterBlock("NewCallLogFragment.onResume");
+ boolean isHidden = isHidden();
+ LogUtil.i("NewVoicemailFragment.onResume", "isHidden = %s", isHidden);
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.attachUi(this);
-
- // TODO(zachh): Consider doing this when fragment becomes visible.
- refreshAnnotatedCallLog(true /* checkDirty */);
+ // As a fragment's onResume() is tied to the containing Activity's onResume(), being resumed is
+ // not equivalent to becoming visible.
+ // For example, when an activity with a hidden fragment is resumed, the fragment's onResume()
+ // will be called but it is not visible.
+ if (!isHidden) {
+ onFragmentShown();
+ }
}
@Override
public void onPause() {
super.onPause();
-
LogUtil.enterBlock("NewVoicemailFragment.onPause");
- // This is pending work that we don't actually need to follow through with.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+ onFragmentHidden();
+ }
- CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
- callLogFramework.detachUi();
+ @Override
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ LogUtil.i("NewVoicemailFragment.onHiddenChanged", "hidden = %s", hidden);
+
+ if (hidden) {
+ onFragmentHidden();
+ } else {
+ onFragmentShown();
+ }
+ }
+
+ /**
+ * To be called when the fragment becomes visible.
+ *
+ * <p>Note that for a fragment, being resumed is not equivalent to becoming visible.
+ *
+ * <p>For example, when an activity with a hidden fragment is resumed, the fragment's onResume()
+ * will be called but it is not visible.
+ */
+ private void onFragmentShown() {
+ registerRefreshAnnotatedCallLogReceiver();
+
+ CallLogComponent.get(getContext())
+ .getRefreshAnnotatedCallLogNotifier()
+ .notify(/* checkDirty = */ true);
+ }
+
+ /**
+ * To be called when the fragment becomes hidden.
+ *
+ * <p>This can happen in the following two cases:
+ *
+ * <ul>
+ * <li>hide the fragment but keep the parent activity visible (e.g., calling {@link
+ * android.support.v4.app.FragmentTransaction#hide(Fragment)} in an activity, or
+ * <li>the parent activity is paused.
+ * </ul>
+ */
+ private void onFragmentHidden() {
+ unregisterRefreshAnnotatedCallLogReceiver();
}
- @Nullable
@Override
public View onCreateView(
LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@@ -139,43 +153,6 @@ public final class NewVoicemailFragment extends Fragment
return view;
}
- private void refreshAnnotatedCallLog(boolean checkDirty) {
- LogUtil.enterBlock("NewVoicemailFragment.refreshAnnotatedCallLog");
-
- // If we already scheduled a refresh, cancel it and schedule a new one so that repeated requests
- // in quick succession don't result in too much work. For example, if we get 10 requests in
- // 10ms, and a complete refresh takes a constant 200ms, the refresh will take 300ms (100ms wait
- // and 1 iteration @200ms) instead of 2 seconds (10 iterations @ 200ms) since the work requests
- // are serialized in RefreshAnnotatedCallLogWorker.
- //
- // We might get many requests in quick succession, for example, when the simulator inserts
- // hundreds of rows into the system call log, or when the data for a new call is incrementally
- // written to different columns as it becomes available.
- ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
-
- refreshAnnotatedCallLogRunnable =
- () -> {
- ListenableFuture<Void> future =
- checkDirty
- ? refreshAnnotatedCallLogWorker.refreshWithDirtyCheck()
- : refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
- refreshAnnotatedCallLogListener.listen(
- getContext(),
- future,
- unused -> {},
- throwable -> {
- throw new RuntimeException(throwable);
- });
- };
- ThreadUtil.getUiThreadHandler().postDelayed(refreshAnnotatedCallLogRunnable, WAIT_MILLIS);
- }
-
- @Override
- public void invalidateUi() {
- LogUtil.enterBlock("NewVoicemailFragment.invalidateUi");
- refreshAnnotatedCallLog(false /* checkDirty */);
- }
-
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
LogUtil.enterBlock("NewVoicemailFragment.onCreateLoader");
@@ -210,6 +187,24 @@ public final class NewVoicemailFragment extends Fragment
queryAndUpdateVoicemailStatusAlert();
}
+ private void registerRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewVoicemailFragment.registerRefreshAnnotatedCallLogReceiver");
+
+ LocalBroadcastManager.getInstance(getContext())
+ .registerReceiver(
+ refreshAnnotatedCallLogReceiver, RefreshAnnotatedCallLogReceiver.getIntentFilter());
+ }
+
+ private void unregisterRefreshAnnotatedCallLogReceiver() {
+ LogUtil.enterBlock("NewVoicemailFragment.unregisterRefreshAnnotatedCallLogReceiver");
+
+ // Cancel pending work as we don't need it any more.
+ CallLogComponent.get(getContext()).getRefreshAnnotatedCallLogNotifier().cancel();
+
+ LocalBroadcastManager.getInstance(getContext())
+ .unregisterReceiver(refreshAnnotatedCallLogReceiver);
+ }
+
private void queryAndUpdateVoicemailStatusAlert() {
queryVoicemailStatusTableListener.listen(
getContext(),
diff --git a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
index 5ae26f5f7..7f5bb796a 100644
--- a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
@@ -32,12 +32,14 @@ import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
+import android.text.Html;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.dialer.notification.NotificationChannelManager;
import com.android.dialer.telecom.TelecomUtil;
+import com.android.dialer.widget.TextViewPreference;
import com.android.voicemail.VoicemailClient;
import com.android.voicemail.VoicemailClient.ActivationStateListener;
import com.android.voicemail.VoicemailComponent;
@@ -73,6 +75,7 @@ public class VoicemailSettingsFragment extends PreferenceFragment
private SwitchPreference donateVoicemailSwitchPreference;
private Preference voicemailChangePinPreference;
private PreferenceScreen advancedSettings;
+ private TextViewPreference voicemailTranscriptionInstructionText;
@Override
public void onCreate(Bundle icicle) {
@@ -209,6 +212,10 @@ public class VoicemailSettingsFragment extends PreferenceFragment
return false;
}
});
+
+ voicemailTranscriptionInstructionText =
+ (TextViewPreference) findPreference(getString(R.string.voicemail_transcription_text_key));
+ voicemailTranscriptionInstructionText.setTitle(getVoicemailTranscriptionInstructionsText());
}
@Override
@@ -348,4 +355,20 @@ public class VoicemailSettingsFragment extends PreferenceFragment
builder.setCancelable(true);
builder.show();
}
+
+ /**
+ * Builds a spannable string containing the voicemail transcription instructions text containing
+ * the appropriate "Learn More" urls.
+ *
+ * @return The voicemail transcription instructions text.
+ */
+ private CharSequence getVoicemailTranscriptionInstructionsText() {
+ String settingText =
+ getString(
+ R.string.voicemail_transcription_instruction_text,
+ getString(R.string.transcription_learn_more_url),
+ getString(R.string.donation_learn_more_url));
+ CharSequence settingSeq = Html.fromHtml(settingText);
+ return settingSeq;
+ }
}
diff --git a/java/com/android/dialer/voicemail/settings/res/values/strings.xml b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
index 10fa459ff..47228b70b 100644
--- a/java/com/android/dialer/voicemail/settings/res/values/strings.xml
+++ b/java/com/android/dialer/voicemail/settings/res/values/strings.xml
@@ -44,7 +44,7 @@
<string name="voicemail_change_pin_key" translatable="false">voicemail_change_pin_key</string>
<!-- Visual voicemail on/off title [CHAR LIMIT=40] -->
- <string name="voicemail_visual_voicemail_switch_title">Visual Voicemail</string>
+ <string name="voicemail_visual_voicemail_switch_title">Visual voicemail</string>
<!-- Visual voicemail archive on/off title [CHAR LIMIT=40] -->
<string name="voicemail_visual_voicemail_auto_archive_switch_title">Extra backup and storage</string>
@@ -125,4 +125,18 @@
<!-- The label for the confirm-disable-voicemail button [CHAR LIMIT=16] -->
<string name="confirm_disable_voicemail_accept_dialog_label">TURN OFF</string>
+ <!-- Internal preferences key for static instruction text. -->
+ <string name="voicemail_transcription_text_key" translatable="false">voicemail_transcription_text_key</string>
+
+ <!-- Additional information text and links for visual voicemail and voicemail donation setting page
+ [CHAR LIMIT=NONE] -->
+ <string name="voicemail_transcription_instruction_text">
+ Visual voicemail allows you to check voicemail messages without having to call voicemail. Transcripts provided by Google. &lt;a href="<xliff:g example="http://www.google.com" id="url1">%1$s</xliff:g>">Learn&#160;more&lt;/a>
+ &lt;br>&lt;br>
+ For voicemail transcription analysis, your voicemail messages are stored anonymously. &lt;a href="<xliff:g example="http://www.google.com" id="url2">%2$s</xliff:g>">Learn&#160;more&lt;/a>
+ </string>
+
+ <string translatable="false" name="transcription_learn_more_url">https://support.google.com/phoneapp/answer/2811844?hl=en%26ref_topic=7539039</string>
+ <string translatable="false" name="donation_learn_more_url">https://support.google.com/phoneapp/answer/2811844#voicemail_transcript</string>
+
</resources>
diff --git a/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml b/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
index 9b0391ad4..75c8cfe2b 100644
--- a/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
+++ b/java/com/android/dialer/voicemail/settings/res/xml/voicemail_settings.xml
@@ -43,4 +43,8 @@
android:key="@string/voicemail_advanced_settings_key"
android:title="@string/voicemail_advanced_settings_title">
</PreferenceScreen>
+
+ <com.android.dialer.widget.TextViewPreference
+ android:key="@string/voicemail_transcription_text_key"/>
+
</PreferenceScreen>
diff --git a/java/com/android/dialer/widget/TextViewPreference.java b/java/com/android/dialer/widget/TextViewPreference.java
new file mode 100644
index 000000000..2c1885c4d
--- /dev/null
+++ b/java/com/android/dialer/widget/TextViewPreference.java
@@ -0,0 +1,141 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.text.method.LinkMovementMethod;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.TextView;
+
+/**
+ * Provides a {@link TextView} inside a preference. Useful for displaying static text which may
+ * contain hyperlinks.
+ */
+public class TextViewPreference extends Preference {
+
+ /**
+ * The resource ID of the text to be populated in the {@link TextView} when a resource ID is used.
+ */
+ private int textResourceId = 0;
+
+ /** The text to be populated in the {@link TextView} when a {@link CharSequence} is used. */
+ private CharSequence text;
+
+ /** The {@link TextView} containing the text. */
+ private TextView textView;
+
+ /**
+ * Instantiates the {@link TextViewPreference} instance.
+ *
+ * @param context The Context this is associated with, through which it can access the current
+ * theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the preference.
+ * @param defStyleAttr An attribute in the current theme that contains a reference to a style
+ * resource that supplies default values for the view. Can be 0 to not look for defaults.
+ * @param defStyleRes A resource identifier of a style resource that supplies default values for
+ * the view, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not
+ * look for defaults.
+ */
+ public TextViewPreference(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ setLayoutResource(R.layout.text_view_preference);
+ }
+
+ /**
+ * Instantiates the {@link TextViewPreference} instance.
+ *
+ * @param context The Context this is associated with, through which it can access the current
+ * theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the preference.
+ * @param defStyleAttr An attribute in the current theme that contains a reference to a style
+ * resource that supplies default values for the view. Can be 0 to not look for defaults.
+ */
+ public TextViewPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ /**
+ * Instantiates the {@link TextViewPreference} instance.
+ *
+ * @param context The Context this is associated with, through which it can access the current
+ * theme, resources, etc.
+ * @param attrs The attributes of the XML tag that is inflating the preference.
+ */
+ public TextViewPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.preferenceStyle, 0);
+ }
+
+ /**
+ * Instantiates the {@link TextViewPreference} instance.
+ *
+ * @param context The Context this is associated with, through which it can access the current
+ * theme, resources, etc.
+ */
+ public TextViewPreference(Context context) {
+ super(context, null);
+
+ setLayoutResource(R.layout.text_view_preference);
+ }
+
+ /**
+ * Handles binding the preference.
+ *
+ * @param view The view.
+ */
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+ textView = (TextView) view.findViewById(R.id.text);
+ if (textResourceId != 0) {
+ setTitle(textResourceId);
+ } else if (text != null) {
+ setTitle(text);
+ }
+ }
+
+ /**
+ * Sets the preference title from a {@link CharSequence}.
+ *
+ * @param text The text.
+ */
+ @Override
+ public void setTitle(CharSequence text) {
+ textResourceId = 0;
+ this.text = text;
+ if (textView == null) {
+ return;
+ }
+
+ textView.setMovementMethod(LinkMovementMethod.getInstance());
+ textView.setText(text);
+ }
+
+ /**
+ * Sets the preference title from a resource id.
+ *
+ * @param textResId The string resource Id.
+ */
+ @Override
+ public void setTitle(int textResId) {
+ textResourceId = textResId;
+ setTitle(getContext().getString(textResId));
+ }
+}
diff --git a/java/com/android/dialer/widget/res/layout/text_view_preference.xml b/java/com/android/dialer/widget/res/layout/text_view_preference.xml
new file mode 100644
index 000000000..39b550657
--- /dev/null
+++ b/java/com/android/dialer/widget/res/layout/text_view_preference.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:fontFamily="sans-serif"
+ android:linksClickable="true"
+ android:singleLine="false"/>
diff --git a/java/com/android/incallui/CallCardPresenter.java b/java/com/android/incallui/CallCardPresenter.java
index da5d1a8dd..b945b0810 100644
--- a/java/com/android/incallui/CallCardPresenter.java
+++ b/java/com/android/incallui/CallCardPresenter.java
@@ -690,7 +690,7 @@ public class CallCardPresenter
if (primary == null) {
// Clear the primary display info.
- inCallScreen.setPrimary(PrimaryInfo.createEmptyPrimaryInfo());
+ inCallScreen.setPrimary(PrimaryInfo.empty());
return;
}
@@ -713,26 +713,22 @@ public class CallCardPresenter
"update primary display info for conference call.");
inCallScreen.setPrimary(
- new PrimaryInfo(
- null /* number */,
- CallerInfoUtils.getConferenceString(
- context, primary.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)),
- false /* nameIsNumber */,
- null /* location */,
- null /* label */,
- null /* photo */,
- ContactPhotoType.DEFAULT_PLACEHOLDER,
- false /* isSipCall */,
- showContactPhoto,
- hasWorkCallProperty,
- false /* isSpam */,
- false /* isLocalContact */,
- false /* answeringDisconnectsOngoingCall */,
- shouldShowLocation(),
- null /* contactInfoLookupKey */,
- null /* enrichedCallMultimediaData */,
- true /* showInCallButtonGrid */,
- primary.getNumberPresentation()));
+ PrimaryInfo.builder()
+ .setName(
+ CallerInfoUtils.getConferenceString(
+ context, primary.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)))
+ .setNameIsNumber(false)
+ .setPhotoType(ContactPhotoType.DEFAULT_PLACEHOLDER)
+ .setIsSipCall(false)
+ .setIsContactPhotoShown(showContactPhoto)
+ .setIsWorkCall(hasWorkCallProperty)
+ .setIsSpam(false)
+ .setIsLocalContact(false)
+ .setAnsweringDisconnectsOngoingCall(false)
+ .setShouldShowLocation(shouldShowLocation())
+ .setShowInCallButtonGrid(true)
+ .setNumberPresentation(primary.getNumberPresentation())
+ .build());
} else if (primaryContactInfo != null) {
LogUtil.v(
"CallCardPresenter.updatePrimaryDisplayInfo",
@@ -761,30 +757,33 @@ public class CallCardPresenter
// DialerCall with caller that is a work contact.
boolean isWorkContact = (primaryContactInfo.userType == ContactsUtils.USER_TYPE_WORK);
inCallScreen.setPrimary(
- new PrimaryInfo(
- number,
- primary.updateNameIfRestricted(name),
- nameIsNumber,
- shouldShowLocationAsLabel(nameIsNumber, primaryContactInfo.shouldShowLocation)
- ? primaryContactInfo.location
- : null,
- isChildNumberShown || isCallSubjectShown ? null : primaryContactInfo.label,
- primaryContactInfo.photo,
- primaryContactInfo.photoType,
- primaryContactInfo.isSipCall,
- showContactPhoto,
- hasWorkCallProperty || isWorkContact,
- primary.isSpam(),
- primaryContactInfo.isLocalContact(),
- primary.answeringDisconnectsForegroundVideoCall(),
- shouldShowLocation(),
- primaryContactInfo.lookupKey,
- multimediaData,
- true /* showInCallButtonGrid */,
- primary.getNumberPresentation()));
+ PrimaryInfo.builder()
+ .setNumber(number)
+ .setName(primary.updateNameIfRestricted(name))
+ .setNameIsNumber(nameIsNumber)
+ .setLabel(
+ shouldShowLocationAsLabel(nameIsNumber, primaryContactInfo.shouldShowLocation)
+ ? primaryContactInfo.location
+ : null)
+ .setLocation(
+ isChildNumberShown || isCallSubjectShown ? null : primaryContactInfo.label)
+ .setPhoto(primaryContactInfo.photo)
+ .setPhotoType(primaryContactInfo.photoType)
+ .setIsSipCall(primaryContactInfo.isSipCall)
+ .setIsContactPhotoShown(showContactPhoto)
+ .setIsWorkCall(hasWorkCallProperty || isWorkContact)
+ .setIsSpam(primary.isSpam())
+ .setIsLocalContact(primaryContactInfo.isLocalContact())
+ .setAnsweringDisconnectsOngoingCall(primary.answeringDisconnectsForegroundVideoCall())
+ .setShouldShowLocation(shouldShowLocation())
+ .setContactInfoLookupKey(primaryContactInfo.lookupKey)
+ .setMultimediaData(multimediaData)
+ .setShowInCallButtonGrid(true)
+ .setNumberPresentation(primary.getNumberPresentation())
+ .build());
} else {
// Clear the primary display info.
- inCallScreen.setPrimary(PrimaryInfo.createEmptyPrimaryInfo());
+ inCallScreen.setPrimary(PrimaryInfo.empty());
}
if (isInCallScreenReady) {
@@ -1192,6 +1191,7 @@ public class CallCardPresenter
return inCallScreen;
}
+ /** Callback for contact lookup. */
public static class ContactLookupCallback implements ContactInfoCacheCallback {
private final WeakReference<CallCardPresenter> callCardPresenter;
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index 67f5cfe4f..f842aedf7 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -61,8 +61,9 @@ import com.android.dialer.compat.ActivityCompat;
import com.android.dialer.compat.CompatUtils;
import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.LoggingBindings;
import com.android.dialer.logging.ScreenEvent;
+import com.android.dialer.metrics.Metrics;
+import com.android.dialer.metrics.MetricsComponent;
import com.android.dialer.util.ViewUtil;
import com.android.incallui.answer.bindings.AnswerBindings;
import com.android.incallui.answer.protocol.AnswerScreen;
@@ -182,6 +183,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity
didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN);
didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN);
didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN);
+ didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN);
}
setWindowFlags();
@@ -248,10 +250,12 @@ public class InCallActivity extends TransactionSafeFragmentActivity
pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
sendBroadcast(CallPendingActivity.getFinishBroadcast());
Trace.endSection();
- Logger.get(this)
- .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
- Logger.get(this)
- .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
+ MetricsComponent.get(this)
+ .metrics()
+ .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
+ MetricsComponent.get(this)
+ .metrics()
+ .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
}
private void setWindowFlags() {
@@ -387,6 +391,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity
out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
+ out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
super.onSaveInstanceState(out);
isVisible = false;
@@ -468,8 +473,9 @@ public class InCallActivity extends TransactionSafeFragmentActivity
// add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
ThreadUtil.postDelayedOnUiThread(
() ->
- Logger.get(this)
- .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
+ MetricsComponent.get(this)
+ .metrics()
+ .recordMemory(Metrics.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
1000);
}
@@ -1593,6 +1599,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity
static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
+ static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
}
/** Request codes for pending intents. */
diff --git a/java/com/android/incallui/RttCallPresenter.java b/java/com/android/incallui/RttCallPresenter.java
index b90d56b36..939c9d00b 100644
--- a/java/com/android/incallui/RttCallPresenter.java
+++ b/java/com/android/incallui/RttCallPresenter.java
@@ -16,28 +16,145 @@
package com.android.incallui;
-import android.content.Context;
+import android.annotation.TargetApi;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.telecom.Call.RttCall;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.ThreadUtil;
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.incallui.InCallPresenter.InCallStateListener;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
import com.android.incallui.rtt.protocol.RttCallScreen;
import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
+import java.io.IOException;
/**
* Logic related to the {@link RttCallScreen} and for managing changes to the RTT calling surfaces
* based on other user interface events and incoming events.
*/
-public class RttCallPresenter implements RttCallScreenDelegate {
+@TargetApi(28)
+public class RttCallPresenter implements RttCallScreenDelegate, InCallStateListener {
- private Context appContext;
private RttCallScreen rttCallScreen;
+ private RttCall rttCall;
+ private HandlerThread handlerThread;
+ private RemoteMessageHandler remoteMessageHandler;
@Override
- public void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen) {
- this.appContext = context.getApplicationContext();
+ public void initRttCallScreenDelegate(RttCallScreen rttCallScreen) {
this.rttCallScreen = rttCallScreen;
}
@Override
- public void onRttCallScreenUiReady() {}
+ public void onLocalMessage(String message) {
+ if (rttCall == null) {
+ LogUtil.w("RttCallPresenter.onLocalMessage", "Rtt Call is not started yet");
+ return;
+ }
+ remoteMessageHandler.writeMessage(message);
+ }
+
+ @Override
+ public void onRttCallScreenUiReady() {
+ LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiReady");
+ InCallPresenter.getInstance().addListener(this);
+ startListenOnRemoteMessage();
+ }
+
+ @Override
+ public void onRttCallScreenUiUnready() {
+ LogUtil.enterBlock("RttCallPresenter.onRttCallScreenUiUnready");
+ InCallPresenter.getInstance().removeListener(this);
+ stopListenOnRemoteMessage();
+ }
@Override
- public void onRttCallScreenUiUnready() {}
+ public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
+ LogUtil.enterBlock("RttCallPresenter.onStateChange");
+ if (newState == InCallState.INCALL) {
+ startListenOnRemoteMessage();
+ }
+ }
+
+ private void startListenOnRemoteMessage() {
+ DialerCall call = CallList.getInstance().getActiveCall();
+ if (call == null) {
+ LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "call is active yet");
+ return;
+ }
+ rttCall = call.getRttCall();
+ if (rttCall == null) {
+ LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "RTT Call is not started yet");
+ return;
+ }
+ if (handlerThread != null && handlerThread.isAlive()) {
+ LogUtil.i("RttCallPresenter.startListenOnRemoteMessage", "already running");
+ return;
+ }
+ handlerThread = new HandlerThread("RttCallRemoteMessageHandler");
+ handlerThread.start();
+ remoteMessageHandler =
+ new RemoteMessageHandler(handlerThread.getLooper(), rttCall, rttCallScreen);
+ remoteMessageHandler.start();
+ }
+
+ private void stopListenOnRemoteMessage() {
+ if (handlerThread != null && handlerThread.isAlive()) {
+ handlerThread.quit();
+ }
+ }
+
+ private static class RemoteMessageHandler extends Handler {
+ private static final int START = 1;
+ private static final int READ_MESSAGE = 2;
+ private static final int WRITE_MESSAGE = 3;
+
+ private final RttCall rttCall;
+ private final RttCallScreen rttCallScreen;
+
+ RemoteMessageHandler(Looper looper, RttCall rttCall, RttCallScreen rttCallScreen) {
+ super(looper);
+ this.rttCall = rttCall;
+ this.rttCallScreen = rttCallScreen;
+ }
+
+ @Override
+ public void handleMessage(android.os.Message msg) {
+ switch (msg.what) {
+ case START:
+ sendEmptyMessage(READ_MESSAGE);
+ break;
+ case READ_MESSAGE:
+ try {
+ final String message = rttCall.readImmediately();
+ if (message != null) {
+ ThreadUtil.postOnUiThread(() -> rttCallScreen.onRemoteMessage(message));
+ }
+ } catch (IOException e) {
+ LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "read message", e);
+ }
+ sendEmptyMessageDelayed(READ_MESSAGE, 200);
+ break;
+ case WRITE_MESSAGE:
+ try {
+ rttCall.write((String) msg.obj);
+ } catch (IOException e) {
+ LogUtil.e("RttCallPresenter.RemoteMessageHandler.handleMessage", "write message", e);
+ }
+ break;
+ default: // fall out
+ }
+ }
+
+ void start() {
+ sendEmptyMessage(START);
+ }
+
+ void writeMessage(String message) {
+ sendMessage(obtainMessage(WRITE_MESSAGE, message));
+ }
+ }
}
diff --git a/java/com/android/incallui/answer/impl/AnswerFragment.java b/java/com/android/incallui/answer/impl/AnswerFragment.java
index 8626e6d0e..3439a3e3c 100644
--- a/java/com/android/incallui/answer/impl/AnswerFragment.java
+++ b/java/com/android/incallui/answer/impl/AnswerFragment.java
@@ -149,7 +149,7 @@ public class AnswerFragment extends Fragment
private boolean buttonAcceptClicked;
private boolean buttonRejectClicked;
private boolean hasAnimatedEntry;
- private PrimaryInfo primaryInfo = PrimaryInfo.createEmptyPrimaryInfo();
+ private PrimaryInfo primaryInfo = PrimaryInfo.empty();
private PrimaryCallState primaryCallState;
private ArrayList<CharSequence> textResponses;
private SmsBottomSheetFragment textResponsesFragment;
@@ -523,13 +523,13 @@ public class AnswerFragment extends Fragment
return;
}
contactGridManager.setPrimary(primaryInfo);
- getAnswerMethod().setShowIncomingWillDisconnect(primaryInfo.answeringDisconnectsOngoingCall);
+ getAnswerMethod().setShowIncomingWillDisconnect(primaryInfo.answeringDisconnectsOngoingCall());
getAnswerMethod()
.setContactPhoto(
- primaryInfo.photoType == ContactPhotoType.CONTACT ? primaryInfo.photo : null);
+ primaryInfo.photoType() == ContactPhotoType.CONTACT ? primaryInfo.photo() : null);
updateDataFragment();
- if (primaryInfo.shouldShowLocation) {
+ if (primaryInfo.shouldShowLocation()) {
// Hide the avatar to make room for location
contactGridManager.setAvatarHidden(true);
}
@@ -562,8 +562,8 @@ public class AnswerFragment extends Fragment
MultimediaFragment.newInstance(
multimediaData,
false /* isInteractive */,
- !primaryInfo.isSpam /* showAvatar */,
- primaryInfo.isSpam);
+ !primaryInfo.isSpam() /* showAvatar */,
+ primaryInfo.isSpam());
}
} else if (shouldShowAvatar()) {
// Needs Avatar
@@ -1067,7 +1067,7 @@ public class AnswerFragment extends Fragment
return;
}
- if (!getResources().getBoolean(R.bool.answer_important_call_allowed) || primaryInfo.isSpam) {
+ if (!getResources().getBoolean(R.bool.answer_important_call_allowed) || primaryInfo.isSpam()) {
importanceBadge.setVisibility(View.GONE);
return;
}
@@ -1088,7 +1088,7 @@ public class AnswerFragment extends Fragment
if (isVideoUpgradeRequest()) {
return null;
}
- return primaryInfo.multimediaData;
+ return primaryInfo.multimediaData();
}
/** Shows the Avatar image if available. */
diff --git a/java/com/android/incallui/call/CallList.java b/java/com/android/incallui/call/CallList.java
index e5948084d..9a0902639 100644
--- a/java/com/android/incallui/call/CallList.java
+++ b/java/com/android/incallui/call/CallList.java
@@ -36,7 +36,8 @@ import com.android.dialer.enrichedcall.EnrichedCallComponent;
import com.android.dialer.enrichedcall.EnrichedCallManager;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
-import com.android.dialer.logging.LoggingBindings;
+import com.android.dialer.metrics.Metrics;
+import com.android.dialer.metrics.MetricsComponent;
import com.android.dialer.shortcuts.ShortcutUsageReporter;
import com.android.dialer.spam.Spam;
import com.android.dialer.spam.SpamComponent;
@@ -119,11 +120,13 @@ public class CallList implements DialerCallDelegate {
final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
Trace.beginSection("CallList.onCallAdded");
if (telecomCall.getState() == Call.STATE_CONNECTING) {
- Logger.get(context)
- .logStartLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
+ MetricsComponent.get(context)
+ .metrics()
+ .startTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
} else if (telecomCall.getState() == Call.STATE_RINGING) {
- Logger.get(context)
- .logStartLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
+ MetricsComponent.get(context)
+ .metrics()
+ .startTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
}
if (uiListeners != null) {
uiListeners.onCallAdded();
diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java
index cbe7c57a6..90a01401c 100644
--- a/java/com/android/incallui/call/DialerCall.java
+++ b/java/com/android/incallui/call/DialerCall.java
@@ -17,6 +17,7 @@
package com.android.incallui.call;
import android.Manifest.permission;
+import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.net.Uri;
@@ -929,6 +930,7 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
return getVideoTech().isTransmittingOrReceiving() || VideoProfile.isVideo(getVideoState());
}
+ @TargetApi(28)
public boolean isRttCall() {
if (BuildCompat.isAtLeastP()) {
return getTelecomCall().isRttActive();
@@ -937,6 +939,14 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
}
}
+ @TargetApi(28)
+ public RttCall getRttCall() {
+ if (!isRttCall()) {
+ return null;
+ }
+ return getTelecomCall().getRttCall();
+ }
+
public boolean hasReceivedVideoUpgradeRequest() {
return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
}
@@ -946,7 +956,6 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa
}
public boolean hasSentRttUpgradeRequest() {
- // TODO(wangqi): Implement this.
return false;
}
diff --git a/java/com/android/incallui/callpending/CallPendingActivity.java b/java/com/android/incallui/callpending/CallPendingActivity.java
index 47325d823..306eed8fb 100644
--- a/java/com/android/incallui/callpending/CallPendingActivity.java
+++ b/java/com/android/incallui/callpending/CallPendingActivity.java
@@ -177,25 +177,25 @@ public class CallPendingActivity extends FragmentActivity
String number = getNumber();
// DialerCall with caller that is a work contact.
- return new PrimaryInfo(
- number,
- name,
- name != null && name.equals(number),
- null /* location */,
- getPhoneLabel(),
- photo,
- ContactPhotoType.CONTACT,
- false /* isSipCall */,
- true /* isContactPhotoShown */,
- false /* isWorkCall */,
- false /* isSpam */,
- true /* isLocalContact */,
- false /* answeringDisconnectsOngoingCall */,
- false /* shouldShowLocation */,
- getLookupKey(),
- multimediaData,
- false /*showInCallButtonGrid */,
- TelecomManager.PRESENTATION_ALLOWED);
+ return PrimaryInfo.builder()
+ .setNumber(number)
+ .setName(name)
+ .setNameIsNumber(name != null && name.equals(number))
+ .setLabel(getPhoneLabel())
+ .setPhoto(photo)
+ .setPhotoType(ContactPhotoType.CONTACT)
+ .setIsSipCall(false)
+ .setIsContactPhotoShown(true)
+ .setIsWorkCall(false)
+ .setIsSpam(false)
+ .setIsLocalContact(true)
+ .setAnsweringDisconnectsOngoingCall(false)
+ .setShouldShowLocation(false)
+ .setContactInfoLookupKey(getLookupKey())
+ .setMultimediaData(multimediaData)
+ .setShowInCallButtonGrid(false)
+ .setNumberPresentation(TelecomManager.PRESENTATION_ALLOWED)
+ .build();
}
@Override
diff --git a/java/com/android/incallui/contactgrid/BottomRow.java b/java/com/android/incallui/contactgrid/BottomRow.java
index f9fc870dc..610eeca39 100644
--- a/java/com/android/incallui/contactgrid/BottomRow.java
+++ b/java/com/android/incallui/contactgrid/BottomRow.java
@@ -82,7 +82,7 @@ public class BottomRow {
boolean isSpamIconVisible = false;
boolean shouldPopulateAccessibilityEvent = true;
- if (isIncoming(state) && primaryInfo.isSpam) {
+ if (isIncoming(state) && primaryInfo.isSpam()) {
label = context.getString(R.string.contact_grid_incoming_suspected_spam);
isSpamIconVisible = true;
isHdIconVisible = false;
@@ -99,7 +99,7 @@ public class BottomRow {
}
} else {
label = getLabelForPhoneNumber(primaryInfo);
- shouldPopulateAccessibilityEvent = primaryInfo.nameIsNumber;
+ shouldPopulateAccessibilityEvent = primaryInfo.nameIsNumber();
}
return new Info(
@@ -114,15 +114,15 @@ public class BottomRow {
}
private static CharSequence getLabelForPhoneNumber(PrimaryInfo primaryInfo) {
- if (primaryInfo.location != null) {
- return primaryInfo.location;
+ if (primaryInfo.location() != null) {
+ return primaryInfo.location();
}
- if (!primaryInfo.nameIsNumber && !TextUtils.isEmpty(primaryInfo.number)) {
- CharSequence spannedNumber = spanDisplayNumber(primaryInfo.number);
- if (primaryInfo.label == null) {
+ if (!primaryInfo.nameIsNumber() && !TextUtils.isEmpty(primaryInfo.number())) {
+ CharSequence spannedNumber = spanDisplayNumber(primaryInfo.number());
+ if (primaryInfo.label() == null) {
return spannedNumber;
} else {
- return TextUtils.concat(primaryInfo.label, " ", spannedNumber);
+ return TextUtils.concat(primaryInfo.label(), " ", spannedNumber);
}
}
return null;
diff --git a/java/com/android/incallui/contactgrid/ContactGridManager.java b/java/com/android/incallui/contactgrid/ContactGridManager.java
index 58d318892..6dec7646f 100644
--- a/java/com/android/incallui/contactgrid/ContactGridManager.java
+++ b/java/com/android/incallui/contactgrid/ContactGridManager.java
@@ -85,7 +85,7 @@ public class ContactGridManager {
private final TextView deviceNumberTextView;
private final View deviceNumberDivider;
- private PrimaryInfo primaryInfo = PrimaryInfo.createEmptyPrimaryInfo();
+ private PrimaryInfo primaryInfo = PrimaryInfo.empty();
private PrimaryCallState primaryCallState = PrimaryCallState.createEmptyPrimaryCallState();
private final LetterTileDrawable letterTile;
private boolean isInMultiWindowMode;
@@ -213,7 +213,7 @@ public class ContactGridManager {
}
boolean hasPhoto =
- primaryInfo.photo != null && primaryInfo.photoType == ContactPhotoType.CONTACT;
+ primaryInfo.photo() != null && primaryInfo.photoType() == ContactPhotoType.CONTACT;
if (!hasPhoto && !showAnonymousAvatar) {
avatarImageView.setVisibility(View.GONE);
return false;
@@ -271,17 +271,17 @@ public class ContactGridManager {
* </ul>
*/
private void updatePrimaryNameAndPhoto() {
- if (TextUtils.isEmpty(primaryInfo.name)) {
+ if (TextUtils.isEmpty(primaryInfo.name())) {
contactNameTextView.setText(null);
} else {
contactNameTextView.setText(
- primaryInfo.nameIsNumber
- ? PhoneNumberUtilsCompat.createTtsSpannable(primaryInfo.name)
- : primaryInfo.name);
+ primaryInfo.nameIsNumber()
+ ? PhoneNumberUtilsCompat.createTtsSpannable(primaryInfo.name())
+ : primaryInfo.name());
// Set direction of the name field
int nameDirection = View.TEXT_DIRECTION_INHERIT;
- if (primaryInfo.nameIsNumber) {
+ if (primaryInfo.nameIsNumber()) {
nameDirection = View.TEXT_DIRECTION_LTR;
}
contactNameTextView.setTextDirection(nameDirection);
@@ -292,23 +292,23 @@ public class ContactGridManager {
avatarImageView.setVisibility(View.GONE);
} else if (avatarSize > 0 && updateAvatarVisibility()) {
boolean hasPhoto =
- primaryInfo.photo != null && primaryInfo.photoType == ContactPhotoType.CONTACT;
+ primaryInfo.photo() != null && primaryInfo.photoType() == ContactPhotoType.CONTACT;
// Contact has a photo, don't render a letter tile.
if (hasPhoto) {
avatarImageView.setBackground(
DrawableConverter.getRoundedDrawable(
- context, primaryInfo.photo, avatarSize, avatarSize));
+ context, primaryInfo.photo(), avatarSize, avatarSize));
// Contact has a name, that isn't a number.
} else {
letterTile.setCanonicalDialerLetterTileDetails(
- primaryInfo.name,
- primaryInfo.contactInfoLookupKey,
+ primaryInfo.name(),
+ primaryInfo.contactInfoLookupKey(),
LetterTileDrawable.SHAPE_CIRCLE,
LetterTileDrawable.getContactTypeFromPrimitives(
primaryCallState.isVoiceMailNumber,
- primaryInfo.isSpam,
+ primaryInfo.isSpam(),
primaryCallState.isBusinessNumber,
- primaryInfo.numberPresentation,
+ primaryInfo.numberPresentation(),
primaryCallState.isConference));
// By invalidating the avatarImageView we force a redraw of the letter tile.
// This is required to properly display the updated letter tile iconography based on the
@@ -413,7 +413,7 @@ public class ContactGridManager {
deviceNumberTextView.setText(
context.getString(R.string.contact_grid_callback_number, primaryCallState.callbackNumber));
deviceNumberTextView.setVisibility(View.VISIBLE);
- if (primaryInfo.shouldShowLocation) {
+ if (primaryInfo.shouldShowLocation()) {
deviceNumberDivider.setVisibility(View.VISIBLE);
}
}
diff --git a/java/com/android/incallui/contactgrid/TopRow.java b/java/com/android/incallui/contactgrid/TopRow.java
index 556b11ba0..5eb38f98c 100644
--- a/java/com/android/incallui/contactgrid/TopRow.java
+++ b/java/com/android/incallui/contactgrid/TopRow.java
@@ -81,7 +81,7 @@ public class TopRow {
// Show phone number if it's not displayed in name (center row) or location field (bottom
// row).
if (shouldShowNumber(primaryInfo, true /* isIncoming */)) {
- label = TextUtils.concat(label, " ", spanDisplayNumber(primaryInfo.number));
+ label = TextUtils.concat(label, " ", spanDisplayNumber(primaryInfo.number()));
}
}
} else if (VideoUtils.hasSentVideoUpgradeRequest(state.sessionModificationState)
@@ -97,7 +97,7 @@ public class TopRow {
label = context.getString(R.string.incall_remotely_held);
} else if (state.state == State.ACTIVE
&& shouldShowNumber(primaryInfo, false /* isIncoming */)) {
- label = spanDisplayNumber(primaryInfo.number);
+ label = spanDisplayNumber(primaryInfo.number());
} else if (state.state == State.CALL_PENDING && !TextUtils.isEmpty(state.customLabel)) {
label = state.customLabel;
} else {
@@ -115,18 +115,18 @@ public class TopRow {
}
private static boolean shouldShowNumber(PrimaryInfo primaryInfo, boolean isIncoming) {
- if (primaryInfo.nameIsNumber) {
+ if (primaryInfo.nameIsNumber()) {
return false;
}
// Don't show number since it's already shown in bottom row of incoming screen if there is no
// location info.
- if (primaryInfo.location == null && isIncoming) {
+ if (primaryInfo.location() == null && isIncoming) {
return false;
}
- if (primaryInfo.isLocalContact && !isIncoming) {
+ if (primaryInfo.isLocalContact() && !isIncoming) {
return false;
}
- if (TextUtils.isEmpty(primaryInfo.number)) {
+ if (TextUtils.isEmpty(primaryInfo.number())) {
return false;
}
return true;
diff --git a/java/com/android/incallui/incall/impl/InCallFragment.java b/java/com/android/incallui/incall/impl/InCallFragment.java
index a4dcd72a8..9a6d1c41e 100644
--- a/java/com/android/incallui/incall/impl/InCallFragment.java
+++ b/java/com/android/incallui/incall/impl/InCallFragment.java
@@ -253,10 +253,10 @@ public class InCallFragment extends Fragment
@Override
public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
LogUtil.i("InCallFragment.setPrimary", primaryInfo.toString());
- setAdapterMedia(primaryInfo.multimediaData, primaryInfo.showInCallButtonGrid);
+ setAdapterMedia(primaryInfo.multimediaData(), primaryInfo.showInCallButtonGrid());
contactGridManager.setPrimary(primaryInfo);
- if (primaryInfo.shouldShowLocation) {
+ if (primaryInfo.shouldShowLocation()) {
// Hide the avatar to make room for location
contactGridManager.setAvatarHidden(true);
diff --git a/java/com/android/incallui/incall/protocol/PrimaryInfo.java b/java/com/android/incallui/incall/protocol/PrimaryInfo.java
index f7457c3ff..63dc39ed3 100644
--- a/java/com/android/incallui/incall/protocol/PrimaryInfo.java
+++ b/java/com/android/incallui/incall/protocol/PrimaryInfo.java
@@ -20,91 +20,116 @@ import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import com.android.dialer.common.LogUtil;
import com.android.dialer.multimedia.MultimediaData;
+import com.google.auto.value.AutoValue;
import java.util.Locale;
/** Information about the primary call. */
-public class PrimaryInfo {
- @Nullable public final String number;
- @Nullable public final String name;
- public final boolean nameIsNumber;
+@AutoValue
+public abstract class PrimaryInfo {
+ @Nullable
+ public abstract String number();
+
+ @Nullable
+ public abstract String name();
+
+ public abstract boolean nameIsNumber();
// This is from contacts and shows the type of number. For example, "Mobile".
- @Nullable public final String label;
- @Nullable public final String location;
- @Nullable public final Drawable photo;
- @ContactPhotoType public final int photoType;
- public final boolean isSipCall;
- public final boolean isContactPhotoShown;
- public final boolean isWorkCall;
- public final boolean isSpam;
- public final boolean isLocalContact;
- public final boolean answeringDisconnectsOngoingCall;
- public final boolean shouldShowLocation;
+ @Nullable
+ public abstract String label();
+
+ @Nullable
+ public abstract String location();
+
+ @Nullable
+ public abstract Drawable photo();
+
+ @ContactPhotoType
+ public abstract int photoType();
+
+ public abstract boolean isSipCall();
+
+ public abstract boolean isContactPhotoShown();
+
+ public abstract boolean isWorkCall();
+
+ public abstract boolean isSpam();
+
+ public abstract boolean isLocalContact();
+
+ public abstract boolean answeringDisconnectsOngoingCall();
+
+ public abstract boolean shouldShowLocation();
// Used for consistent LetterTile coloring.
- @Nullable public final String contactInfoLookupKey;
- @Nullable public final MultimediaData multimediaData;
- public final boolean showInCallButtonGrid;
- public final int numberPresentation;
-
- // TODO: Convert to autovalue. a bug
- public static PrimaryInfo createEmptyPrimaryInfo() {
- return new PrimaryInfo(
- null,
- null,
- false,
- null,
- null,
- null,
- ContactPhotoType.DEFAULT_PLACEHOLDER,
- false,
- false,
- false,
- false,
- false,
- false,
- false,
- null,
- null,
- true,
- -1);
+ @Nullable
+ public abstract String contactInfoLookupKey();
+
+ @Nullable
+ public abstract MultimediaData multimediaData();
+
+ public abstract boolean showInCallButtonGrid();
+
+ public abstract int numberPresentation();
+
+ public static Builder builder() {
+ return new AutoValue_PrimaryInfo.Builder();
+ }
+ /** Builder class for primary call info. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder setNumber(String number);
+
+ public abstract Builder setName(String name);
+
+ public abstract Builder setNameIsNumber(boolean nameIsNumber);
+
+ public abstract Builder setLabel(String label);
+
+ public abstract Builder setLocation(String location);
+
+ public abstract Builder setPhoto(Drawable photo);
+
+ public abstract Builder setPhotoType(@ContactPhotoType int photoType);
+
+ public abstract Builder setIsSipCall(boolean isSipCall);
+
+ public abstract Builder setIsContactPhotoShown(boolean isContactPhotoShown);
+
+ public abstract Builder setIsWorkCall(boolean isWorkCall);
+
+ public abstract Builder setIsSpam(boolean isSpam);
+
+ public abstract Builder setIsLocalContact(boolean isLocalContact);
+
+ public abstract Builder setAnsweringDisconnectsOngoingCall(
+ boolean answeringDisconnectsOngoingCall);
+
+ public abstract Builder setShouldShowLocation(boolean shouldShowLocation);
+
+ public abstract Builder setContactInfoLookupKey(String contactInfoLookupKey);
+
+ public abstract Builder setMultimediaData(MultimediaData multimediaData);
+
+ public abstract Builder setShowInCallButtonGrid(boolean showInCallButtonGrid);
+
+ public abstract Builder setNumberPresentation(int numberPresentation);
+
+ public abstract PrimaryInfo build();
}
- public PrimaryInfo(
- @Nullable String number,
- @Nullable String name,
- boolean nameIsNumber,
- @Nullable String location,
- @Nullable String label,
- @Nullable Drawable photo,
- @ContactPhotoType int phototType,
- boolean isSipCall,
- boolean isContactPhotoShown,
- boolean isWorkCall,
- boolean isSpam,
- boolean isLocalContact,
- boolean answeringDisconnectsOngoingCall,
- boolean shouldShowLocation,
- @Nullable String contactInfoLookupKey,
- @Nullable MultimediaData multimediaData,
- boolean showInCallButtonGrid,
- int numberPresentation) {
- this.number = number;
- this.name = name;
- this.nameIsNumber = nameIsNumber;
- this.location = location;
- this.label = label;
- this.photo = photo;
- this.photoType = phototType;
- this.isSipCall = isSipCall;
- this.isContactPhotoShown = isContactPhotoShown;
- this.isWorkCall = isWorkCall;
- this.isSpam = isSpam;
- this.isLocalContact = isLocalContact;
- this.answeringDisconnectsOngoingCall = answeringDisconnectsOngoingCall;
- this.shouldShowLocation = shouldShowLocation;
- this.contactInfoLookupKey = contactInfoLookupKey;
- this.multimediaData = multimediaData;
- this.showInCallButtonGrid = showInCallButtonGrid;
- this.numberPresentation = numberPresentation;
+ public static PrimaryInfo empty() {
+ return PrimaryInfo.builder()
+ .setNameIsNumber(false)
+ .setPhotoType(ContactPhotoType.DEFAULT_PLACEHOLDER)
+ .setIsSipCall(false)
+ .setIsContactPhotoShown(false)
+ .setIsWorkCall(false)
+ .setIsSpam(false)
+ .setIsLocalContact(false)
+ .setAnsweringDisconnectsOngoingCall(false)
+ .setShouldShowLocation(false)
+ .setShowInCallButtonGrid(true)
+ .setNumberPresentation(-1)
+ .build();
}
@Override
@@ -113,13 +138,13 @@ public class PrimaryInfo {
Locale.US,
"PrimaryInfo, number: %s, name: %s, location: %s, label: %s, "
+ "photo: %s, photoType: %d, isPhotoVisible: %b, MultimediaData: %s",
- LogUtil.sanitizePhoneNumber(number),
- LogUtil.sanitizePii(name),
- LogUtil.sanitizePii(location),
- label,
- photo,
- photoType,
- isContactPhotoShown,
- multimediaData);
+ LogUtil.sanitizePhoneNumber(number()),
+ LogUtil.sanitizePii(name()),
+ LogUtil.sanitizePii(location()),
+ label(),
+ photo(),
+ photoType(),
+ isContactPhotoShown(),
+ multimediaData());
}
}
diff --git a/java/com/android/incallui/rtt/impl/RttChatAdapter.java b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
index 1ea7f31b1..69837188a 100644
--- a/java/com/android/incallui/rtt/impl/RttChatAdapter.java
+++ b/java/com/android/incallui/rtt/impl/RttChatAdapter.java
@@ -110,7 +110,17 @@ public class RttChatAdapter extends RecyclerView.Adapter<RttChatMessageViewHolde
notifyItemInserted(lastIndexOfLocalMessage);
} else {
rttChatMessage.append(newMessage);
- notifyItemChanged(lastIndexOfLocalMessage);
+ // Clear empty message bubble.
+ if (TextUtils.isEmpty(rttChatMessage.getContent())) {
+ rttMessages.remove(lastIndexOfLocalMessage);
+ notifyItemRemoved(lastIndexOfLocalMessage);
+ if (lastIndexOfRemoteMessage > lastIndexOfLocalMessage) {
+ lastIndexOfRemoteMessage -= 1;
+ }
+ lastIndexOfLocalMessage = -1;
+ } else {
+ notifyItemChanged(lastIndexOfLocalMessage);
+ }
}
}
diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java
index c7ee2ff67..a33029501 100644
--- a/java/com/android/incallui/rtt/impl/RttChatFragment.java
+++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java
@@ -132,7 +132,7 @@ public class RttChatFragment extends Fragment
FragmentUtils.getParentUnsafe(this, RttCallScreenDelegateFactory.class)
.newRttCallScreenDelegate(this);
- rttCallScreenDelegate.initRttCallScreenDelegate(getContext(), this);
+ rttCallScreenDelegate.initRttCallScreenDelegate(this);
inCallScreenDelegate.onInCallScreenDelegateInit(this);
inCallScreenDelegate.onInCallScreenReady();
@@ -193,7 +193,24 @@ public class RttChatFragment extends Fragment
if (isClearingInput) {
return;
}
- adapter.addLocalMessage(RttChatMessage.getChangedString(s, start, before, count));
+ String messageToAppend = RttChatMessage.getChangedString(s, start, before, count);
+ if (!TextUtils.isEmpty(messageToAppend)) {
+ adapter.addLocalMessage(messageToAppend);
+ rttCallScreenDelegate.onLocalMessage(messageToAppend);
+ }
+ }
+
+ @Override
+ public void onRemoteMessage(String message) {
+ adapter.addRemoteMessage(message);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ LogUtil.enterBlock("RttChatFragment.onDestroyView");
+ inCallButtonUiDelegate.onInCallButtonUiUnready();
+ inCallScreenDelegate.onInCallScreenUnready();
}
@Override
@@ -260,7 +277,7 @@ public class RttChatFragment extends Fragment
@Override
public void setPrimary(@NonNull PrimaryInfo primaryInfo) {
LogUtil.i("RttChatFragment.setPrimary", primaryInfo.toString());
- nameTextView.setText(primaryInfo.name);
+ nameTextView.setText(primaryInfo.name());
}
@Override
diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreen.java b/java/com/android/incallui/rtt/protocol/RttCallScreen.java
index afacbae48..916dfb84d 100644
--- a/java/com/android/incallui/rtt/protocol/RttCallScreen.java
+++ b/java/com/android/incallui/rtt/protocol/RttCallScreen.java
@@ -25,6 +25,8 @@ public interface RttCallScreen {
void onRttScreenStop();
+ void onRemoteMessage(String message);
+
Fragment getRttCallScreenFragment();
String getCallId();
diff --git a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
index e29c43d70..8c484a844 100644
--- a/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
+++ b/java/com/android/incallui/rtt/protocol/RttCallScreenDelegate.java
@@ -16,14 +16,14 @@
package com.android.incallui.rtt.protocol;
-import android.content.Context;
-
/** Callbacks from the module out to the container. */
public interface RttCallScreenDelegate {
- void initRttCallScreenDelegate(Context context, RttCallScreen rttCallScreen);
+ void initRttCallScreenDelegate(RttCallScreen rttCallScreen);
void onRttCallScreenUiReady();
void onRttCallScreenUiUnready();
+
+ void onLocalMessage(String message);
}
diff --git a/java/com/android/voicemail/impl/res/xml/vvm_config.xml b/java/com/android/voicemail/impl/res/xml/vvm_config.xml
index ed7761abe..4c5efcc1c 100644
--- a/java/com/android/voicemail/impl/res/xml/vvm_config.xml
+++ b/java/com/android/voicemail/impl/res/xml/vvm_config.xml
@@ -123,6 +123,21 @@
</pbundle_as_map>
<pbundle_as_map>
+ <!-- tracfone USA-->
+ <string name="feature_flag_name">vvm_carrier_flag_tracfone_usa</string>
+
+ <string-array name="mccmnc">
+ <item value="310260?gid1=4d4b"/>
+ <item value="310260?gid1=534d"/>
+ <item value="310260?gid1=6134"/>
+ <item value="310260?gid1=ddff"/>
+ <item value="310260?gid1=deff"/>
+ </string-array>
+
+ <string name="vvm_type_string">vvm_type_disable</string>>
+ </pbundle_as_map>
+
+ <pbundle_as_map>
<!-- Telus Canada -->
<string name="feature_flag_name">vvm_carrier_flag_302220</string>
<string-array name="mccmnc">