summaryrefslogtreecommitdiff
path: root/java
diff options
context:
space:
mode:
authorroldenburg <roldenburg@google.com>2017-11-30 11:16:31 -0800
committerEric Erfanian <erfanian@google.com>2017-11-30 20:03:37 +0000
commit1632cfe1e3f3f94295289a62c69ec5a0489d8f13 (patch)
tree6c9b41286d91b802c169e734aba430eca815da1a /java
parent49842c4701b9aa9f998fdc92e0ec505d1d99e777 (diff)
*** Reason for rollback ***
Copybara is fixed for AOSP export Bug: 68665330 Test: rollback PiperOrigin-RevId: 177480870 Change-Id: I0ba38e213bb840436fa6dafc4af0a79019ee93f4
Diffstat (limited to 'java')
-rw-r--r--java/com/android/dialer/app/res/values/styles.xml5
-rw-r--r--java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java61
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java38
-rw-r--r--java/com/android/dialer/common/concurrent/DialerExecutorComponent.java11
-rw-r--r--java/com/android/dialer/common/concurrent/UiListener.java143
-rw-r--r--java/com/android/dialer/common/concurrent/UiThreadExecutor.java4
-rw-r--r--java/com/android/dialer/dialpadview/DialpadFragment.java31
-rw-r--r--java/com/android/dialer/precall/externalreceiver/LaunchPreCallActivity.java10
-rw-r--r--java/com/android/incallui/contactgrid/ContactGridManager.java1
9 files changed, 258 insertions, 46 deletions
diff --git a/java/com/android/dialer/app/res/values/styles.xml b/java/com/android/dialer/app/res/values/styles.xml
index c26821023..2d47eaf94 100644
--- a/java/com/android/dialer/app/res/values/styles.xml
+++ b/java/com/android/dialer/app/res/values/styles.xml
@@ -21,7 +21,7 @@
<item name="android:colorAccent">@color/dialtacts_theme_color</item>
</style>
- <style name="DialtactsTheme" parent="DialerThemeBase">
+ <style name="DialtactsThemeBase" parent="DialerThemeBase">
<!-- Styles that require AppCompat compatibility, remember to update both sets -->
<item name="android:windowActionBarOverlay">true</item>
@@ -77,6 +77,9 @@
<item name="dialpad_style">@style/Dialpad.Light</item>
</style>
+ <style name="DialtactsTheme" parent="DialtactsThemeBase">
+ </style>
+
<!-- Action bar overflow menu icon. White with no shadow. -->
<style name="DialtactsActionBarOverflowWhite"
parent="@android:style/Widget.Material.Light.ActionButton.Overflow">
diff --git a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
index 72f73cdaf..d9924b23f 100644
--- a/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
+++ b/java/com/android/dialer/calllog/RefreshAnnotatedCallLogWorker.java
@@ -29,39 +29,74 @@ import com.android.dialer.calllog.datasources.CallLogMutations;
import com.android.dialer.calllog.datasources.DataSources;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+import com.android.dialer.common.concurrent.Annotations.UiSerial;
import com.android.dialer.inject.ApplicationContext;
import com.android.dialer.storage.Unencrypted;
+import com.google.common.util.concurrent.ListenableScheduledFuture;
+import com.google.common.util.concurrent.ListeningScheduledExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
-/**
- * Worker which brings the annotated call log up to date, if necessary.
- *
- * <p>Accepts a boolean which indicates if the dirty check should be skipped.
- */
-public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Void> {
+/** Brings the annotated call log up to date, if necessary. */
+public class RefreshAnnotatedCallLogWorker {
+
+ /*
+ * 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 final Context appContext;
private final DataSources dataSources;
private final SharedPreferences sharedPreferences;
+ private final ListeningScheduledExecutorService listeningScheduledExecutorService;
+ private ListenableScheduledFuture<Void> scheduledFuture;
@Inject
RefreshAnnotatedCallLogWorker(
@ApplicationContext Context appContext,
DataSources dataSources,
- @Unencrypted SharedPreferences sharedPreferences) {
+ @Unencrypted SharedPreferences sharedPreferences,
+ @UiSerial ScheduledExecutorService serialUiExecutorService) {
this.appContext = appContext;
this.dataSources = dataSources;
this.sharedPreferences = sharedPreferences;
+ this.listeningScheduledExecutorService =
+ MoreExecutors.listeningDecorator(serialUiExecutorService);
}
- @Override
- public Void doInBackground(Boolean skipDirtyCheck)
+ /** Checks if the annotated call log is dirty and refreshes it if necessary. */
+ public ListenableScheduledFuture<Void> refreshWithDirtyCheck() {
+ return refresh(true);
+ }
+
+ /** Refreshes the annotated call log, bypassing dirty checks. */
+ public ListenableScheduledFuture<Void> refreshWithoutDirtyCheck() {
+ return refresh(false);
+ }
+
+ private ListenableScheduledFuture<Void> refresh(boolean checkDirty) {
+ if (scheduledFuture != null) {
+ LogUtil.i("RefreshAnnotatedCallLogWorker.refresh", "cancelling waiting task");
+ scheduledFuture.cancel(false /* mayInterrupt */);
+ }
+ scheduledFuture =
+ listeningScheduledExecutorService.schedule(
+ () -> doInBackground(checkDirty), WAIT_MILLIS, TimeUnit.MILLISECONDS);
+ return scheduledFuture;
+ }
+
+ @WorkerThread
+ private Void doInBackground(boolean checkDirty)
throws RemoteException, OperationApplicationException {
LogUtil.enterBlock("RefreshAnnotatedCallLogWorker.doInBackground");
long startTime = System.currentTimeMillis();
- checkDirtyAndRebuildIfNecessary(appContext, skipDirtyCheck);
+ checkDirtyAndRebuildIfNecessary(appContext, checkDirty);
LogUtil.i(
"RefreshAnnotatedCallLogWorker.doInBackground",
"took %dms",
@@ -70,7 +105,7 @@ public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Void> {
}
@WorkerThread
- private void checkDirtyAndRebuildIfNecessary(Context appContext, boolean skipDirtyCheck)
+ private void checkDirtyAndRebuildIfNecessary(Context appContext, boolean checkDirty)
throws RemoteException, OperationApplicationException {
Assert.isWorkerThread();
@@ -86,7 +121,7 @@ public class RefreshAnnotatedCallLogWorker implements Worker<Boolean, Void> {
"annotated call log has been marked dirty or does not exist");
}
- boolean isDirty = skipDirtyCheck || forceRebuildPrefValue || isDirty(appContext);
+ boolean isDirty = !checkDirty || forceRebuildPrefValue || isDirty(appContext);
LogUtil.i(
"RefreshAnnotatedCallLogWorker.checkDirtyAndRebuildIfNecessary",
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index ab7381347..6833452c6 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -28,24 +28,18 @@ 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.common.LogUtil;
-import com.android.dialer.common.concurrent.DialerExecutor;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.common.concurrent.DialerExecutorFactory;
+import com.android.dialer.common.concurrent.UiListener;
+import com.google.common.util.concurrent.ListenableScheduledFuture;
/** 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 WAIT_MILLIS = 100L;
-
- private DialerExecutor<Boolean> refreshAnnotatedCallLogTask;
+ private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
+ private UiListener<Void> refreshAnnotatedCallLogListener;
private RecyclerView recyclerView;
public NewCallLogFragment() {
@@ -62,17 +56,12 @@ public final class NewCallLogFragment extends Fragment
CallLogFramework callLogFramework = component.callLogFramework();
callLogFramework.attachUi(this);
- DialerExecutorFactory dialerExecutorFactory =
- DialerExecutorComponent.get(getContext()).dialerExecutorFactory();
-
// TODO(zachh): Use support fragment manager and add support for them in executors library.
- refreshAnnotatedCallLogTask =
- dialerExecutorFactory
- .createUiTaskBuilder(
- getActivity().getFragmentManager(),
- "NewCallLogFragment.refreshAnnotatedCallLog",
- component.getRefreshAnnotatedCallLogWorker())
- .build();
+ refreshAnnotatedCallLogListener =
+ DialerExecutorComponent.get(getContext())
+ .createUiListener(
+ getActivity().getFragmentManager(), "NewCallLogFragment.refreshAnnotatedCallLog");
+ refreshAnnotatedCallLogWorker = component.getRefreshAnnotatedCallLogWorker();
}
@Override
@@ -120,13 +109,16 @@ public final class NewCallLogFragment extends Fragment
private void checkAnnotatedCallLogDirtyAndRefreshIfNecessary() {
LogUtil.enterBlock("NewCallLogFragment.checkAnnotatedCallLogDirtyAndRefreshIfNecessary");
- refreshAnnotatedCallLogTask.executeSerialWithWait(false /* skipDirtyCheck */, WAIT_MILLIS);
+ ListenableScheduledFuture<Void> future = refreshAnnotatedCallLogWorker.refreshWithDirtyCheck();
+ refreshAnnotatedCallLogListener.listen(future, unused -> {}, RuntimeException::new);
}
@Override
public void invalidateUi() {
LogUtil.enterBlock("NewCallLogFragment.invalidateUi");
- refreshAnnotatedCallLogTask.executeSerialWithWait(true /* skipDirtyCheck */, WAIT_MILLIS);
+ ListenableScheduledFuture<Void> future =
+ refreshAnnotatedCallLogWorker.refreshWithoutDirtyCheck();
+ refreshAnnotatedCallLogListener.listen(future, unused -> {}, RuntimeException::new);
}
@Override
diff --git a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
index 346fdda56..7ee30a083 100644
--- a/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
+++ b/java/com/android/dialer/common/concurrent/DialerExecutorComponent.java
@@ -16,9 +16,12 @@
package com.android.dialer.common.concurrent;
+import android.app.FragmentManager;
import android.content.Context;
import com.android.dialer.common.concurrent.Annotations.NonUiParallel;
+import com.android.dialer.common.concurrent.Annotations.Ui;
import com.android.dialer.inject.HasRootComponent;
+import com.google.common.util.concurrent.ListeningExecutorService;
import dagger.Subcomponent;
import java.util.concurrent.ExecutorService;
@@ -28,6 +31,14 @@ public abstract class DialerExecutorComponent {
public abstract DialerExecutorFactory dialerExecutorFactory();
+ @Ui
+ public abstract ListeningExecutorService uiExecutorService();
+
+ public <OutputT> UiListener<OutputT> createUiListener(
+ FragmentManager fragmentManager, String taskId) {
+ return UiListener.create(uiExecutorService(), fragmentManager, taskId);
+ }
+
@NonUiParallel
public abstract ExecutorService lowPriorityThreadPool();
diff --git a/java/com/android/dialer/common/concurrent/UiListener.java b/java/com/android/dialer/common/concurrent/UiListener.java
new file mode 100644
index 000000000..11302d299
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/UiListener.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.common.concurrent;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.Assert;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor.FailureListener;
+import com.android.dialer.common.concurrent.DialerExecutor.SuccessListener;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.concurrent.Executor;
+
+/**
+ * A headless fragment for use in UI components that interact with ListenableFutures.
+ *
+ * <p>Callbacks are only executed if the UI component is still alive.
+ *
+ * <p>Example usage: <code><pre>
+ * public class MyActivity extends Activity {
+ *
+ * private UiListener&lt;MyOutputType&gt uiListener;
+ *
+ * public void onCreate(Bundle bundle) {
+ * super.onCreate(bundle);
+ *
+ * // Must be called in onCreate!
+ * uiListener = DialerExecutorComponent.get(context).createUiListener(fragmentManager, taskId);
+ * }
+ *
+ * private void onSuccess(MyOutputType output) { ... }
+ * private void onFailure(Throwable throwable) { ... }
+ *
+ * private void userDidSomething() {
+ * ListenableFuture&lt;MyOutputType&gt; future = callSomeMethodReturningListenableFuture(input);
+ * uiListener.listen(future, this::onSuccess, this::onFailure);
+ * }
+ * }
+ * </pre></code>
+ */
+public class UiListener<OutputT> extends Fragment {
+
+ private Executor uiThreadExecutor;
+ private CallbackWrapper<OutputT> callbackWrapper;
+
+ @MainThread
+ static <OutputT> UiListener<OutputT> create(
+ Executor uiThreadExecutor, FragmentManager fragmentManager, String taskId) {
+ @SuppressWarnings("unchecked")
+ UiListener<OutputT> uiListener =
+ (UiListener<OutputT>) fragmentManager.findFragmentByTag(taskId);
+
+ if (uiListener == null) {
+ LogUtil.i("UiListener.create", "creating new UiListener for " + taskId);
+ uiListener = new UiListener<>();
+ uiListener.uiThreadExecutor = uiThreadExecutor;
+ fragmentManager.beginTransaction().add(uiListener, taskId).commit();
+ }
+ return uiListener;
+ }
+
+ /**
+ * Adds the specified listeners to the provided future.
+ *
+ * <p>The listeners are not called if the UI component this {@link UiListener} is declared in is
+ * dead.
+ */
+ @MainThread
+ public void listen(
+ @NonNull ListenableFuture<OutputT> future,
+ @NonNull SuccessListener<OutputT> successListener,
+ @NonNull FailureListener failureListener) {
+ callbackWrapper =
+ new CallbackWrapper<>(Assert.isNotNull(successListener), Assert.isNotNull(failureListener));
+ Futures.addCallback(Assert.isNotNull(future), callbackWrapper, uiThreadExecutor);
+ }
+
+ private static class CallbackWrapper<OutputT> implements FutureCallback<OutputT> {
+ private SuccessListener<OutputT> successListener;
+ private FailureListener failureListener;
+
+ private CallbackWrapper(
+ SuccessListener<OutputT> successListener, FailureListener failureListener) {
+ this.successListener = successListener;
+ this.failureListener = failureListener;
+ }
+
+ @Override
+ public void onSuccess(@Nullable OutputT output) {
+ if (successListener == null) {
+ LogUtil.i("UiListener.runTask", "task succeeded but UI is dead");
+ } else {
+ successListener.onSuccess(output);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LogUtil.e("UiListener.runTask", "task failed", throwable);
+ if (failureListener == null) {
+ LogUtil.i("UiListener.runTask", "task failed but UI is dead");
+ } else {
+ failureListener.onFailure(throwable);
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ LogUtil.enterBlock("UiListener.onDetach");
+ if (callbackWrapper != null) {
+ callbackWrapper.successListener = null;
+ callbackWrapper.failureListener = null;
+ }
+ }
+}
diff --git a/java/com/android/dialer/common/concurrent/UiThreadExecutor.java b/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
index 7b95d70ea..ec51ed334 100644
--- a/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
+++ b/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
@@ -21,6 +21,7 @@ import com.google.common.util.concurrent.SettableFuture;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
+import javax.inject.Inject;
/**
* An ExecutorService that delegates to the UI thread. Rejects attempts to shut down, and all
@@ -29,6 +30,9 @@ import java.util.concurrent.TimeUnit;
*/
public class UiThreadExecutor extends AbstractListeningExecutorService {
+ @Inject
+ UiThreadExecutor() {}
+
@Override
public void shutdown() {
throw new UnsupportedOperationException();
diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java
index 69186d87c..4673d9eca 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -49,6 +49,7 @@ import android.telephony.PhoneNumberFormattingTextWatcher;
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.text.Editable;
+import android.text.Selection;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
@@ -89,6 +90,7 @@ import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.CallUtil;
import com.android.dialer.util.PermissionsUtil;
import com.android.dialer.widget.FloatingActionButtonController;
+import com.google.common.base.Ascii;
import com.google.common.base.Optional;
import java.util.HashSet;
import java.util.List;
@@ -1882,13 +1884,21 @@ public class DialpadFragment extends Fragment
@Override
public synchronized void afterTextChanged(Editable s) {
- super.afterTextChanged(s);
-
- if (!"AR".equals(countryCode)) {
+ // When the country code is NOT "AR", Android telephony's PhoneNumberFormattingTextWatcher can
+ // correctly handle the input so we will let it do its job.
+ if (!Ascii.toUpperCase(countryCode).equals("AR")) {
+ super.afterTextChanged(s);
return;
}
+ // When the country code is "AR", PhoneNumberFormattingTextWatcher can also format the input
+ // correctly if the number is NOT for a domestic call to a mobile phone.
String rawNumber = getRawNumber(s);
+ Matcher matcher = AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN.matcher(rawNumber);
+ if (!matcher.matches()) {
+ super.afterTextChanged(s);
+ return;
+ }
// As modifying the input will trigger another call to afterTextChanged(Editable), we must
// check whether the input's format has already been removed and return if it has
@@ -1897,11 +1907,16 @@ public class DialpadFragment extends Fragment
return;
}
- Matcher matcher = AR_DOMESTIC_CALL_MOBILE_NUMBER_PATTERN.matcher(rawNumber);
- if (matcher.matches()) {
- s.replace(0, s.length(), rawNumber);
- PhoneNumberUtils.addTtsSpan(s, 0 /* start */, s.length() /* endExclusive */);
- }
+ // If we reach this point, the country code must be "AR" and variable "s" represents a number
+ // for a domestic call to a mobile phone. "s" is incorrectly formatted by Android telephony's
+ // PhoneNumberFormattingTextWatcher so we remove its format by replacing it with the raw
+ // number.
+ s.replace(0, s.length(), rawNumber);
+
+ // Make sure the cursor is at the end of the text.
+ Selection.setSelection(s, s.length());
+
+ PhoneNumberUtils.addTtsSpan(s, 0 /* start */, s.length() /* endExclusive */);
}
private static String getRawNumber(Editable s) {
diff --git a/java/com/android/dialer/precall/externalreceiver/LaunchPreCallActivity.java b/java/com/android/dialer/precall/externalreceiver/LaunchPreCallActivity.java
index f79546e54..6096d5192 100644
--- a/java/com/android/dialer/precall/externalreceiver/LaunchPreCallActivity.java
+++ b/java/com/android/dialer/precall/externalreceiver/LaunchPreCallActivity.java
@@ -21,6 +21,8 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
import com.android.dialer.callintent.CallInitiationType.Type;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.configprovider.ConfigProvider;
@@ -56,8 +58,14 @@ public class LaunchPreCallActivity extends Activity {
ConfigProvider configProvider = ConfigProviderBindings.get(getApplicationContext());
Intent intent = getIntent();
CallIntentBuilder builder = new CallIntentBuilder(intent.getData(), Type.EXTERNAL_INITIATION);
+
+ PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE);
+ if (phoneAccountHandle == null) {
+ phoneAccountHandle = intent.getParcelableExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
+ }
+
builder
- .setPhoneAccountHandle(intent.getParcelableExtra(EXTRA_PHONE_ACCOUNT_HANDLE))
+ .setPhoneAccountHandle(phoneAccountHandle)
.setIsVideoCall(intent.getBooleanExtra(EXTRA_IS_VIDEO_CALL, false))
.setCallSubject(intent.getStringExtra(EXTRA_CALL_SUBJECT))
.setAllowAssistedDial(
diff --git a/java/com/android/incallui/contactgrid/ContactGridManager.java b/java/com/android/incallui/contactgrid/ContactGridManager.java
index 1bac97842..6574aa82f 100644
--- a/java/com/android/incallui/contactgrid/ContactGridManager.java
+++ b/java/com/android/incallui/contactgrid/ContactGridManager.java
@@ -375,6 +375,7 @@ public class ContactGridManager {
} else {
forwardIconImageView.setVisibility(View.GONE);
forwardedNumberView.setVisibility(View.GONE);
+ bottomTextSwitcher.setVisibility(View.VISIBLE);
}
if (info.isTimerVisible) {