summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2017-11-18 09:08:07 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-11-18 09:08:07 +0000
commit45cb4415b652a12d3c58523109e399e3ac66a152 (patch)
tree9bd404bcedcee822bd69c91d55fc94e22361f7d1
parenta2b4ed6f4340b959e2542e98a6fc2395475827c3 (diff)
parente53711736e4da5ae7f722e54182f1cb51082d45c (diff)
Merge changes I8cb0c6e7,I03258e86,I0b31bd93,I866708a6,I84f9e19b, ...
* changes: Implement change SIM animation Implement headless PreCall Merge the following methods in InCallActivityCommon into InCallActivity: 1. maybeShowErrorDialogOnDisconnect(DisconnectMessage), 2. onDialogDismissed(), 3. showErrorDialog(Dialog, CharSequence), 4. showInternationalCallOnWifiDialog(DialerCall), 5. showWifiFailedDialog(DialerCall), and 6. showWifiToLteHandoverToast(DialerCall). Added PhoneLookupDataSource and implemented isDirty. Send SUB_ID_EXTRA to VoicemailSettingsActivity in Telephony Add preferred SIM meta-data to dialer manifest Remove unused code InCallUiBings.getUiReadyBroadcastIntent(). Move the following methods from InCallActivityCommon to InCallActivity: 1. enableInCallOrientationEventListener(boolean) 2. isDialpadVisible() 3. getDialpadFragment() Keyboard now closes in regular search when user scrolls list. Optimize contact search by using a Ternary Search Tree. Dialpad FAB no longer loses it's icon on fragment resume. Automated rollback Move InCallActivityCommon#dismissPendingDialogs to InCallActivity. Automated rollback Replace NUI voicemail media buttons with image buttons Notify content URI when preferred SIM is updated Use phone account ID for legacy voicemail notification. Move InCallActivityCommon#dismissKeyGuard to InCallActivity.
-rw-r--r--assets/quantum/res/drawable/quantum_ic_delete_vd_theme_24.xml25
-rw-r--r--assets/quantum/res/drawable/quantum_ic_play_arrow_vd_theme_24.xml25
-rw-r--r--assets/quantum/res/drawable/quantum_ic_volume_up_vd_theme_24.xml25
-rw-r--r--java/com/android/dialer/app/AndroidManifest.xml1
-rw-r--r--java/com/android/dialer/app/DialtactsActivity.java30
-rw-r--r--java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java20
-rw-r--r--java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java2
-rw-r--r--java/com/android/dialer/calllog/CallLogModule.java17
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java8
-rw-r--r--java/com/android/dialer/calllog/datasources/DataSources.java6
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java139
-rw-r--r--java/com/android/dialer/dialpadview/DialpadFragment.java2
-rw-r--r--java/com/android/dialer/dialpadview/res/layout/dialpad_fragment.xml1
-rw-r--r--java/com/android/dialer/phonelookup/testing/FakePhoneLookup.java83
-rw-r--r--java/com/android/dialer/precall/PreCallAction.java20
-rw-r--r--java/com/android/dialer/precall/PreCallCoordinator.java13
-rw-r--r--java/com/android/dialer/precall/impl/AssistedDialAction.java17
-rw-r--r--java/com/android/dialer/precall/impl/CallingAccountSelector.java23
-rw-r--r--java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java2
-rw-r--r--java/com/android/dialer/precall/impl/PreCallImpl.java20
-rw-r--r--java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java1
-rw-r--r--java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java2
-rw-r--r--java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java88
-rw-r--r--java/com/android/dialer/searchfragment/cp2/ContactTernarySearchTree.java97
-rw-r--r--java/com/android/dialer/searchfragment/list/NewSearchFragment.java5
-rw-r--r--java/com/android/dialer/telecom/TelecomUtil.java21
-rw-r--r--java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java22
-rw-r--r--java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_media_player_layout.xml42
-rw-r--r--java/com/android/dialer/voicemail/listui/res/values/dimens.xml5
-rw-r--r--java/com/android/dialer/voicemail/listui/res/values/styles.xml25
-rw-r--r--java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java29
-rw-r--r--java/com/android/dialer/widget/FloatingActionButtonController.java12
-rw-r--r--java/com/android/incallui/InCallActivity.java201
-rw-r--r--java/com/android/incallui/InCallActivityCommon.java279
-rw-r--r--java/com/android/incallui/InCallOrientationEventListener.java15
-rw-r--r--java/com/android/incallui/InCallPresenter.java61
-rw-r--r--java/com/android/incallui/bindings/InCallUiBindings.java4
-rw-r--r--java/com/android/incallui/bindings/InCallUiBindingsStub.java6
-rw-r--r--java/com/android/incallui/incall/impl/ButtonController.java6
-rw-r--r--java/com/android/incallui/incall/impl/CheckableLabeledButton.java10
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_00.pngbin0 -> 860 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_01.pngbin0 -> 942 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_02.pngbin0 -> 886 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_03.pngbin0 -> 822 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_04.pngbin0 -> 885 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_05.pngbin0 -> 915 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_06.pngbin0 -> 911 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_07.pngbin0 -> 907 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_08.pngbin0 -> 945 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_09.pngbin0 -> 917 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_10.pngbin0 -> 973 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_11.pngbin0 -> 950 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_12.pngbin0 -> 972 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_13.pngbin0 -> 940 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_14.pngbin0 -> 872 bytes
-rw-r--r--java/com/android/incallui/incall/impl/res/drawable/ic_sim_change_white.xml68
56 files changed, 1083 insertions, 395 deletions
diff --git a/assets/quantum/res/drawable/quantum_ic_delete_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_delete_vd_theme_24.xml
new file mode 100644
index 000000000..900b559e3
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_delete_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+</vector> \ No newline at end of file
diff --git a/assets/quantum/res/drawable/quantum_ic_play_arrow_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_play_arrow_vd_theme_24.xml
new file mode 100644
index 000000000..e17e625a5
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_play_arrow_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M8,5v14l11,-7z"/>
+</vector> \ No newline at end of file
diff --git a/assets/quantum/res/drawable/quantum_ic_volume_up_vd_theme_24.xml b/assets/quantum/res/drawable/quantum_ic_volume_up_vd_theme_24.xml
new file mode 100644
index 000000000..ac14beced
--- /dev/null
+++ b/assets/quantum/res/drawable/quantum_ic_volume_up_vd_theme_24.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="?attr/colorControlNormal"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/>
+</vector> \ No newline at end of file
diff --git a/java/com/android/dialer/app/AndroidManifest.xml b/java/com/android/dialer/app/AndroidManifest.xml
index ad771496d..12abaa6a9 100644
--- a/java/com/android/dialer/app/AndroidManifest.xml
+++ b/java/com/android/dialer/app/AndroidManifest.xml
@@ -140,5 +140,6 @@
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
+ <meta-data android:name="supports_per_number_preferred_account" android:value="true" />
</application>
</manifest>
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java
index 269e598e1..d9a63fab2 100644
--- a/java/com/android/dialer/app/DialtactsActivity.java
+++ b/java/com/android/dialer/app/DialtactsActivity.java
@@ -96,6 +96,7 @@ import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.callintent.CallSpecificAppData;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.UiUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.configprovider.ConfigProviderBindings;
import com.android.dialer.constants.ActivityRequestCodes;
@@ -178,6 +179,7 @@ public class DialtactsActivity extends TransactionSafeActivity
private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
private static final String KEY_IN_NEW_SEARCH_UI = "in_new_search_ui";
private static final String KEY_SEARCH_QUERY = "search_query";
+ private static final String KEY_DIALPAD_QUERY = "dialpad_query";
private static final String KEY_FIRST_LAUNCH = "first_launch";
private static final String KEY_WAS_CONFIGURATION_CHANGE = "was_configuration_change";
private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
@@ -439,6 +441,7 @@ public class DialtactsActivity extends TransactionSafeActivity
.commit();
} else {
mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
+ mDialpadQuery = savedInstanceState.getString(KEY_DIALPAD_QUERY);
mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
mInNewSearch = savedInstanceState.getBoolean(KEY_IN_NEW_SEARCH_UI);
@@ -654,6 +657,7 @@ public class DialtactsActivity extends TransactionSafeActivity
LogUtil.enterBlock("DialtactsActivity.onSaveInstanceState");
super.onSaveInstanceState(outState);
outState.putString(KEY_SEARCH_QUERY, mSearchQuery);
+ outState.putString(KEY_DIALPAD_QUERY, mDialpadQuery);
outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
outState.putBoolean(KEY_IN_NEW_SEARCH_UI, mInNewSearch);
@@ -1425,6 +1429,21 @@ public class DialtactsActivity extends TransactionSafeActivity
}
@Override
+ public boolean onSearchListTouch(MotionEvent event) {
+ if (mIsDialpadShown) {
+ PerformanceReport.recordClick(UiAction.Type.CLOSE_DIALPAD);
+ hideDialpadFragment(true, false);
+ if (TextUtils.isEmpty(mDialpadQuery)) {
+ exitSearchUi();
+ }
+ return true;
+ } else {
+ UiUtil.hideKeyboardFrom(this, mSearchEditTextLayout);
+ }
+ return false;
+ }
+
+ @Override
public void onListFragmentScrollStateChange(int scrollState) {
PerformanceReport.recordScrollStateChange(scrollState);
if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
@@ -1659,17 +1678,6 @@ public class DialtactsActivity extends TransactionSafeActivity
}
@Override
- public boolean onSearchListTouch(MotionEvent event) {
- if (mIsDialpadShown) {
- hideDialpadFragment(true, false);
- if (TextUtils.isEmpty(mDialpadQuery)) {
- exitSearchUi();
- }
- }
- return false;
- }
-
- @Override
public void onCallPlaced() {
if (mIsDialpadShown) {
hideDialpadFragment(false, true);
diff --git a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
index 584f07fe3..a0bbfa0f1 100644
--- a/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
+++ b/java/com/android/dialer/app/calllog/LegacyVoicemailNotifier.java
@@ -40,6 +40,7 @@ import com.android.dialer.notification.NotificationChannelManager;
/** Shows a notification in the status bar for legacy vociemail. */
@TargetApi(VERSION_CODES.O)
public final class LegacyVoicemailNotifier {
+ private static final String NOTIFICATION_TAG_PREFIX = "LegacyVoicemail_";
private static final String NOTIFICATION_TAG = "LegacyVoicemail";
private static final int NOTIFICATION_ID = 1;
@@ -77,7 +78,8 @@ public final class LegacyVoicemailNotifier {
callVoicemailIntent,
voicemailSettingsIntent,
isRefresh);
- DialerNotificationManager.notify(context, NOTIFICATION_TAG, NOTIFICATION_ID, notification);
+ DialerNotificationManager.notify(
+ context, getNotificationTag(context, handle), NOTIFICATION_ID, notification);
}
@NonNull
@@ -146,10 +148,22 @@ public final class LegacyVoicemailNotifier {
}
}
- public static void cancelNotification(@NonNull Context context) {
+ public static void cancelNotification(
+ @NonNull Context context, @NonNull PhoneAccountHandle phoneAccountHandle) {
LogUtil.enterBlock("LegacyVoicemailNotifier.cancelNotification");
Assert.checkArgument(BuildCompat.isAtLeastO());
- DialerNotificationManager.cancel(context, NOTIFICATION_TAG, NOTIFICATION_ID);
+ Assert.isNotNull(phoneAccountHandle);
+ DialerNotificationManager.cancel(
+ context, getNotificationTag(context, phoneAccountHandle), NOTIFICATION_ID);
+ }
+
+ @NonNull
+ private static String getNotificationTag(
+ @NonNull Context context, @NonNull PhoneAccountHandle phoneAccountHandle) {
+ if (context.getSystemService(TelephonyManager.class).getPhoneCount() <= 1) {
+ return NOTIFICATION_TAG;
+ }
+ return NOTIFICATION_TAG_PREFIX + phoneAccountHandle.getId();
}
private LegacyVoicemailNotifier() {}
diff --git a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
index 3ce837b8c..fee845469 100644
--- a/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
+++ b/java/com/android/dialer/app/voicemail/LegacyVoicemailNotificationReceiver.java
@@ -96,7 +96,7 @@ public class LegacyVoicemailNotificationReceiver extends BroadcastReceiver {
if (count == 0) {
LogUtil.i("LegacyVoicemailNotificationReceiver.onReceive", "clearing notification");
- LegacyVoicemailNotifier.cancelNotification(context);
+ LegacyVoicemailNotifier.cancelNotification(context, phoneAccountHandle);
return;
}
diff --git a/java/com/android/dialer/calllog/CallLogModule.java b/java/com/android/dialer/calllog/CallLogModule.java
index 2f2f16d5b..9926cebb9 100644
--- a/java/com/android/dialer/calllog/CallLogModule.java
+++ b/java/com/android/dialer/calllog/CallLogModule.java
@@ -19,12 +19,11 @@ package com.android.dialer.calllog;
import com.android.dialer.calllog.datasources.CallLogDataSource;
import com.android.dialer.calllog.datasources.DataSources;
import com.android.dialer.calllog.datasources.contacts.ContactsDataSource;
+import com.android.dialer.calllog.datasources.phonelookup.PhoneLookupDataSource;
import com.android.dialer.calllog.datasources.systemcalllog.SystemCallLogDataSource;
+import com.google.common.collect.ImmutableList;
import dagger.Module;
import dagger.Provides;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
/** Dagger module which satisfies call log dependencies. */
@Module
@@ -32,10 +31,12 @@ public abstract class CallLogModule {
@Provides
static DataSources provideCallLogDataSources(
- SystemCallLogDataSource systemCallLogDataSource, ContactsDataSource contactsDataSource) {
+ SystemCallLogDataSource systemCallLogDataSource,
+ ContactsDataSource contactsDataSource,
+ PhoneLookupDataSource phoneLookupDataSource) {
// System call log must be first, see getDataSourcesExcludingSystemCallLog below.
- List<CallLogDataSource> allDataSources =
- Collections.unmodifiableList(Arrays.asList(systemCallLogDataSource, contactsDataSource));
+ ImmutableList<CallLogDataSource> allDataSources =
+ ImmutableList.of(systemCallLogDataSource, contactsDataSource, phoneLookupDataSource);
return new DataSources() {
@Override
public SystemCallLogDataSource getSystemCallLogDataSource() {
@@ -43,12 +44,12 @@ public abstract class CallLogModule {
}
@Override
- public List<CallLogDataSource> getDataSourcesIncludingSystemCallLog() {
+ public ImmutableList<CallLogDataSource> getDataSourcesIncludingSystemCallLog() {
return allDataSources;
}
@Override
- public List<CallLogDataSource> getDataSourcesExcludingSystemCallLog() {
+ public ImmutableList<CallLogDataSource> getDataSourcesExcludingSystemCallLog() {
return allDataSources.subList(1, allDataSources.size());
}
};
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
index 3062710d4..0d8e8ceeb 100644
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
@@ -89,6 +89,13 @@ class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper {
+ AnnotatedCallLog.CALL_TYPE
+ ");";
+ private static final String CREATE_INDEX_ON_NUMBER_SQL =
+ "create index number_index on "
+ + AnnotatedCallLog.TABLE
+ + " ("
+ + AnnotatedCallLog.NUMBER
+ + ");";
+
@Override
public void onCreate(SQLiteDatabase db) {
LogUtil.enterBlock("AnnotatedCallLogDatabaseHelper.onCreate");
@@ -96,6 +103,7 @@ class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(CREATE_TABLE_SQL);
db.execSQL(String.format(Locale.US, CREATE_TRIGGER_SQL, maxRows, maxRows));
db.execSQL(CREATE_INDEX_ON_CALL_TYPE_SQL);
+ db.execSQL(CREATE_INDEX_ON_NUMBER_SQL);
// TODO(zachh): Consider logging impression.
LogUtil.i(
"AnnotatedCallLogDatabaseHelper.onCreate",
diff --git a/java/com/android/dialer/calllog/datasources/DataSources.java b/java/com/android/dialer/calllog/datasources/DataSources.java
index 911ca3fa3..113a9f7b1 100644
--- a/java/com/android/dialer/calllog/datasources/DataSources.java
+++ b/java/com/android/dialer/calllog/datasources/DataSources.java
@@ -17,14 +17,14 @@
package com.android.dialer.calllog.datasources;
import com.android.dialer.calllog.datasources.systemcalllog.SystemCallLogDataSource;
-import java.util.List;
+import com.google.common.collect.ImmutableList;
/** Immutable lists of data sources used to populate the annotated call log. */
public interface DataSources {
SystemCallLogDataSource getSystemCallLogDataSource();
- List<CallLogDataSource> getDataSourcesIncludingSystemCallLog();
+ ImmutableList<CallLogDataSource> getDataSourcesIncludingSystemCallLog();
- List<CallLogDataSource> getDataSourcesExcludingSystemCallLog();
+ ImmutableList<CallLogDataSource> getDataSourcesExcludingSystemCallLog();
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
new file mode 100644
index 000000000..90298a104
--- /dev/null
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -0,0 +1,139 @@
+/*
+ * 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.calllog.datasources.phonelookup;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.support.annotation.MainThread;
+import android.support.annotation.WorkerThread;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog;
+import com.android.dialer.calllog.datasources.CallLogDataSource;
+import com.android.dialer.calllog.datasources.CallLogMutations;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.storage.Unencrypted;
+import com.google.common.collect.ImmutableSet;
+import com.google.protobuf.InvalidProtocolBufferException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+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 {
+ private static final String PREF_LAST_TIMESTAMP_PROCESSED = "phoneLookupLastTimestampProcessed";
+
+ private final PhoneLookup phoneLookup;
+ private final SharedPreferences sharedPreferences;
+
+ @Inject
+ PhoneLookupDataSource(PhoneLookup phoneLookup, @Unencrypted SharedPreferences sharedPreferences) {
+ this.phoneLookup = phoneLookup;
+ this.sharedPreferences = sharedPreferences;
+ }
+
+ @WorkerThread
+ @Override
+ public boolean isDirty(Context appContext) {
+ ImmutableSet<DialerPhoneNumber> uniqueDialerPhoneNumbers =
+ queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(appContext);
+
+ long lastTimestampProcessedSharedPrefValue =
+ sharedPreferences.getLong(PREF_LAST_TIMESTAMP_PROCESSED, 0L);
+ try {
+ // TODO(zachh): Would be good to rework call log architecture to properly use futures.
+ // TODO(zachh): Consider how individual lookups should behave wrt timeouts/exceptions and
+ // handle appropriately here.
+ return phoneLookup
+ .isDirty(uniqueDialerPhoneNumbers, lastTimestampProcessedSharedPrefValue)
+ .get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ @WorkerThread
+ @Override
+ public void fill(Context appContext, CallLogMutations mutations) {
+ // TODO(zachh): Implementation.
+ }
+
+ @WorkerThread
+ @Override
+ public void onSuccessfulFill(Context appContext) {
+ // TODO(zachh): Implementation.
+ }
+
+ @WorkerThread
+ @Override
+ public ContentValues coalesce(List<ContentValues> individualRowsSortedByTimestampDesc) {
+ // TODO(zachh): Implementation.
+ return new ContentValues();
+ }
+
+ @MainThread
+ @Override
+ public void registerContentObservers(
+ Context appContext, ContentObserverCallbacks contentObserverCallbacks) {
+ // No content observers required for this data source.
+ }
+
+ private static ImmutableSet<DialerPhoneNumber>
+ queryDistinctDialerPhoneNumbersFromAnnotatedCallLog(Context appContext) {
+ ImmutableSet.Builder<DialerPhoneNumber> numbers = ImmutableSet.builder();
+
+ try (Cursor cursor =
+ appContext
+ .getContentResolver()
+ .query(
+ AnnotatedCallLog.DISTINCT_NUMBERS_CONTENT_URI,
+ new String[] {AnnotatedCallLog.NUMBER},
+ null,
+ null,
+ null)) {
+
+ if (cursor == null) {
+ LogUtil.e(
+ "PhoneLookupDataSource.queryDistinctDialerPhoneNumbersFromAnnotatedCallLog",
+ "null cursor");
+ return numbers.build();
+ }
+
+ if (cursor.moveToFirst()) {
+ int numberColumn = cursor.getColumnIndexOrThrow(AnnotatedCallLog.NUMBER);
+ do {
+ byte[] blob = cursor.getBlob(numberColumn);
+ if (blob == null) {
+ // Not all [incoming] calls have associated phone numbers.
+ continue;
+ }
+ try {
+ numbers.add(DialerPhoneNumber.parseFrom(blob));
+ } catch (InvalidProtocolBufferException e) {
+ throw new IllegalStateException(e);
+ }
+ } while (cursor.moveToNext());
+ }
+ }
+ return numbers.build();
+ }
+}
diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java
index e85b57e33..8d11bcbe3 100644
--- a/java/com/android/dialer/dialpadview/DialpadFragment.java
+++ b/java/com/android/dialer/dialpadview/DialpadFragment.java
@@ -643,7 +643,7 @@ public class DialpadFragment extends Fragment
iconId = R.drawable.ic_wifi_calling;
}
mFloatingActionButtonController.changeIcon(
- res.getDrawable(iconId, null), res.getString(R.string.description_dial_button));
+ iconId, res.getString(R.string.description_dial_button));
mDialpadQueryListener =
FragmentUtils.getParentUnsafe(this, OnDialpadQueryChangedListener.class);
diff --git a/java/com/android/dialer/dialpadview/res/layout/dialpad_fragment.xml b/java/com/android/dialer/dialpadview/res/layout/dialpad_fragment.xml
index 2f62e1407..2e6b6eca0 100644
--- a/java/com/android/dialer/dialpadview/res/layout/dialpad_fragment.xml
+++ b/java/com/android/dialer/dialpadview/res/layout/dialpad_fragment.xml
@@ -66,6 +66,7 @@
android:layout_centerHorizontal="true"
android:contentDescription="@string/description_dial_button"
android:src="@drawable/quantum_ic_call_vd_theme_24"
+ android:tint="#ffffff"
app:backgroundTint="@color/dialpad_fab_green"
app:colorControlNormal="#ffffff"
app:elevation="@dimen/floating_action_button_translation_z"/>
diff --git a/java/com/android/dialer/phonelookup/testing/FakePhoneLookup.java b/java/com/android/dialer/phonelookup/testing/FakePhoneLookup.java
new file mode 100644
index 000000000..853116f9a
--- /dev/null
+++ b/java/com/android/dialer/phonelookup/testing/FakePhoneLookup.java
@@ -0,0 +1,83 @@
+/*
+ * 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.phonelookup.testing;
+
+import android.support.annotation.NonNull;
+import android.telecom.Call;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.phonelookup.PhoneLookup;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+
+/** Fake implementation of {@link PhoneLookup} used for unit tests. */
+@AutoValue
+public abstract class FakePhoneLookup implements PhoneLookup {
+
+ abstract PhoneLookupInfo lookupResult();
+
+ abstract ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> bulkUpdateResult();
+
+ abstract boolean isDirtyResult();
+
+ public static Builder builder() {
+ return new AutoValue_FakePhoneLookup.Builder()
+ .setLookupResult(PhoneLookupInfo.getDefaultInstance())
+ .setBulkUpdateResult(ImmutableMap.of())
+ .setIsDirtyResult(false);
+ }
+
+ /** Builder. */
+ @AutoValue.Builder
+ public abstract static class Builder {
+
+ public abstract Builder setLookupResult(PhoneLookupInfo phoneLookupInfo);
+
+ public abstract Builder setBulkUpdateResult(
+ ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> map);
+
+ public abstract Builder setIsDirtyResult(boolean isDirty);
+
+ public abstract FakePhoneLookup build();
+ }
+
+ @Override
+ public ListenableFuture<PhoneLookupInfo> lookup(@NonNull Call call) {
+ SettableFuture<PhoneLookupInfo> future = SettableFuture.create();
+ future.set(lookupResult());
+ return future;
+ }
+
+ @Override
+ public ListenableFuture<Boolean> isDirty(
+ ImmutableSet<DialerPhoneNumber> phoneNumbers, long lastModified) {
+ SettableFuture<Boolean> future = SettableFuture.create();
+ future.set(isDirtyResult());
+ return future;
+ }
+
+ @Override
+ public ListenableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> bulkUpdate(
+ ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> existingInfoMap, long lastModified) {
+ SettableFuture<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> future =
+ SettableFuture.create();
+ future.set(bulkUpdateResult());
+ return future;
+ }
+}
diff --git a/java/com/android/dialer/precall/PreCallAction.java b/java/com/android/dialer/precall/PreCallAction.java
index 9434694a4..9ddc6f205 100644
--- a/java/com/android/dialer/precall/PreCallAction.java
+++ b/java/com/android/dialer/precall/PreCallAction.java
@@ -16,6 +16,7 @@
package com.android.dialer.precall;
+import android.content.Context;
import android.support.annotation.MainThread;
import com.android.dialer.callintent.CallIntentBuilder;
@@ -28,12 +29,29 @@ import com.android.dialer.callintent.CallIntentBuilder;
public interface PreCallAction {
/**
+ * Whether the action requires an activity to operate. This method is called on all actions before
+ * {@link #runWithUi(PreCallCoordinator)} is called. If {@link true} is returned, {@link
+ * #runWithUi(PreCallCoordinator)} will be guaranteed to be called on the execution phase.
+ * Otherwise {@link #runWithoutUi(Context, CallIntentBuilder)} may be called instead and the
+ * action will not be able to show UI, perform async task, or abort the call. This method should
+ * not make any state changes.
+ */
+ @MainThread
+ boolean requiresUi(Context context, CallIntentBuilder builder);
+
+ /**
+ * Called when all actions returned {@code false} for {@link #requiresUi(Context,
+ * CallIntentBuilder)}.
+ */
+ void runWithoutUi(Context context, CallIntentBuilder builder);
+
+ /**
* Runs the action. Should block on the main thread until the action is finished. If the action is
* not instantaneous, {@link PreCallCoordinator#startPendingAction()} should be called to release
* the thread and continue later.
*/
@MainThread
- void run(PreCallCoordinator coordinator);
+ void runWithUi(PreCallCoordinator coordinator);
/**
* Called when the UI is being paused when a {@link PreCallCoordinator.PendingAction} is started,
diff --git a/java/com/android/dialer/precall/PreCallCoordinator.java b/java/com/android/dialer/precall/PreCallCoordinator.java
index 40b909a51..cb3221afd 100644
--- a/java/com/android/dialer/precall/PreCallCoordinator.java
+++ b/java/com/android/dialer/precall/PreCallCoordinator.java
@@ -33,10 +33,7 @@ public interface PreCallCoordinator {
@NonNull
CallIntentBuilder getBuilder();
- /**
- * @return the activity to attach the UI to. Returns {@link null} if the coordinator is running on
- * headless mode. TODO(twyen): implement headless mode.
- */
+ /** @return the activity to attach the UI to. */
@NonNull
Activity getActivity();
@@ -60,10 +57,10 @@ public interface PreCallCoordinator {
* Called by the current running {@link PreCallAction} to release the main thread and resume
* pre-call later.
*
- * @return a {@link PendingAction} which {@link PendingAction#finish(boolean)} should be called to
- * resume pre-call. For example the action shows a dialog to the user, startPendingAction()
- * should be called as the action will not be finished immediately. When the dialog is
- * completed, {@code finish()} is then called to continue the next step.
+ * @return a {@link PendingAction} which {@link PendingAction#finish()} should be called to resume
+ * pre-call. For example the action shows a dialog to the user, startPendingAction() should be
+ * called as the action will not be finished immediately. When the dialog is completed, {@code
+ * finish()} is then called to continue the next step.
*/
@MainThread
@NonNull
diff --git a/java/com/android/dialer/precall/impl/AssistedDialAction.java b/java/com/android/dialer/precall/impl/AssistedDialAction.java
index edf97cce3..c4f61d2dd 100644
--- a/java/com/android/dialer/precall/impl/AssistedDialAction.java
+++ b/java/com/android/dialer/precall/impl/AssistedDialAction.java
@@ -17,6 +17,7 @@
package com.android.dialer.precall.impl;
import android.annotation.TargetApi;
+import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.telecom.PhoneAccount;
@@ -35,18 +36,21 @@ import java.util.Optional;
/** Rewrites the call URI with country code. TODO(erfanian): use phone account for multi SIM */
public class AssistedDialAction implements PreCallAction {
+ @Override
+ public boolean requiresUi(Context context, CallIntentBuilder builder) {
+ return false;
+ }
+
@SuppressWarnings("AndroidApiChecker") // Use of optional
@TargetApi(Build.VERSION_CODES.N)
@Override
- public void run(PreCallCoordinator coordinator) {
- CallIntentBuilder builder = coordinator.getBuilder();
+ public void runWithoutUi(Context context, CallIntentBuilder builder) {
if (!builder.isAssistedDialAllowed()) {
return;
}
AssistedDialingMediator assistedDialingMediator =
ConcreteCreator.createNewAssistedDialingMediator(
- coordinator.getActivity().getSystemService(TelephonyManager.class),
- coordinator.getActivity());
+ context.getSystemService(TelephonyManager.class), context);
if (!assistedDialingMediator.isPlatformEligible()) {
return;
}
@@ -69,5 +73,10 @@ public class AssistedDialAction implements PreCallAction {
}
@Override
+ public void runWithUi(PreCallCoordinator coordinator) {
+ runWithoutUi(coordinator.getActivity(), coordinator.getBuilder());
+ }
+
+ @Override
public void onDiscard() {}
}
diff --git a/java/com/android/dialer/precall/impl/CallingAccountSelector.java b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
index ca74bef08..d763c7a5f 100644
--- a/java/com/android/dialer/precall/impl/CallingAccountSelector.java
+++ b/java/com/android/dialer/precall/impl/CallingAccountSelector.java
@@ -59,16 +59,27 @@ public class CallingAccountSelector implements PreCallAction {
private boolean isDiscarding;
@Override
- @MainThread
- public void run(PreCallCoordinator coordinator) {
- CallIntentBuilder builder = coordinator.getBuilder();
+ public boolean requiresUi(Context context, CallIntentBuilder builder) {
if (builder.getPhoneAccountHandle() != null) {
- return;
+ return false;
}
- Activity activity = coordinator.getActivity();
- TelecomManager telecomManager = activity.getSystemService(TelecomManager.class);
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
List<PhoneAccountHandle> accounts = telecomManager.getCallCapablePhoneAccounts();
if (accounts.size() <= 1) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void runWithoutUi(Context context, CallIntentBuilder builder) {
+ // do nothing.
+ }
+
+ @Override
+ public void runWithUi(PreCallCoordinator coordinator) {
+ CallIntentBuilder builder = coordinator.getBuilder();
+ if (!requiresUi(coordinator.getActivity(), builder)) {
return;
}
switch (builder.getUri().getScheme()) {
diff --git a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
index 6302a2395..485823e9a 100644
--- a/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallCoordinatorImpl.java
@@ -93,7 +93,7 @@ public class PreCallCoordinatorImpl implements PreCallCoordinator {
}
LogUtil.i("PreCallCoordinatorImpl.runNextAction", "running " + actions.get(currentActionIndex));
currentAction = actions.get(currentActionIndex);
- actions.get(currentActionIndex).run(this);
+ actions.get(currentActionIndex).runWithUi(this);
if (pendingAction == null) {
onActionFinished();
}
diff --git a/java/com/android/dialer/precall/impl/PreCallImpl.java b/java/com/android/dialer/precall/impl/PreCallImpl.java
index 21c5dc9e2..1c78bb8b0 100644
--- a/java/com/android/dialer/precall/impl/PreCallImpl.java
+++ b/java/com/android/dialer/precall/impl/PreCallImpl.java
@@ -20,8 +20,10 @@ import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.precall.PreCall;
import com.android.dialer.precall.PreCallAction;
+import com.android.dialer.precall.PreCallComponent;
import com.android.dialer.precall.PreCallCoordinator;
import com.google.common.collect.ImmutableList;
import javax.inject.Inject;
@@ -40,8 +42,26 @@ public class PreCallImpl implements PreCall {
@NonNull
@Override
public Intent buildIntent(Context context, CallIntentBuilder builder) {
+ if (!requiresUi(context, builder)) {
+ LogUtil.i("PreCallImpl.buildIntent", "No UI requested, running pre-call directly");
+ for (PreCallAction action : PreCallComponent.get(context).getPreCall().getActions()) {
+ action.runWithoutUi(context, builder);
+ }
+ return builder.build();
+ }
+ LogUtil.i("PreCallImpl.buildIntent", "building intent to start activity");
Intent intent = new Intent(context, PreCallActivity.class);
intent.putExtra(PreCallCoordinator.EXTRA_CALL_INTENT_BUILDER, builder);
return intent;
}
+
+ private boolean requiresUi(Context context, CallIntentBuilder builder) {
+ for (PreCallAction action : PreCallComponent.get(context).getPreCall().getActions()) {
+ if (action.requiresUi(context, builder)) {
+ LogUtil.i("PreCallImpl.requiresUi", action + " requested UI");
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java b/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java
index 322baa2ef..1b10765a5 100644
--- a/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java
+++ b/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java
@@ -131,6 +131,7 @@ public class PreferredSimFallbackProvider extends ContentProvider {
== -1) {
throw new IllegalStateException("update failed");
}
+ getContext().getContentResolver().notifyChange(PreferredSimFallbackContract.CONTENT_URI, null);
return 1;
}
diff --git a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
index 1ecb486d2..c4ff27545 100644
--- a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
+++ b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
@@ -137,7 +137,7 @@ public class QueryFilteringUtil {
* @param context The context
* @return The original string with characters replaced with T9 representations.
*/
- static String getT9Representation(String s, Context context) {
+ public static String getT9Representation(String s, Context context) {
StringBuilder builder = new StringBuilder(s.length());
for (char c : s.toLowerCase().toCharArray()) {
builder.append(getDigit(c, context));
diff --git a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
index 166902b2b..dc16f9dd0 100644
--- a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
+++ b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
@@ -39,6 +39,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
import java.util.Set;
/**
@@ -52,6 +53,7 @@ final class ContactFilterCursor implements Cursor {
private final Cursor cursor;
// List of cursor ids that are valid for displaying after filtering.
private final List<Integer> queryFilteredPositions = new ArrayList<>();
+ private final ContactTernarySearchTree contactTree;
private int currentPosition = 0;
@@ -77,6 +79,7 @@ final class ContactFilterCursor implements Cursor {
*/
ContactFilterCursor(Cursor cursor, @Nullable String query, Context context) {
this.cursor = createCursor(cursor);
+ contactTree = buildContactSearchTree(context, this.cursor);
filter(query, context);
}
@@ -225,6 +228,69 @@ final class ContactFilterCursor implements Cursor {
}
/**
+ * Returns a ternary search trie based on the contact at the cursor's current position with the
+ * following terms inserted:
+ *
+ * <ul>
+ * <li>Contact's whole display name, company name and nickname.
+ * <li>The T9 representations of those values
+ * <li>The T9 initials of those values
+ * <li>All possible substrings a contact's phone number
+ * </ul>
+ */
+ private static ContactTernarySearchTree buildContactSearchTree(Context context, Cursor cursor) {
+ ContactTernarySearchTree tree = new ContactTernarySearchTree();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ int position = cursor.getPosition();
+ Set<String> queryMatches = new ArraySet<>();
+ addMatches(context, queryMatches, cursor.getString(Projections.DISPLAY_NAME));
+ addMatches(context, queryMatches, cursor.getString(Projections.COMPANY_NAME));
+ addMatches(context, queryMatches, cursor.getString(Projections.NICKNAME));
+ for (String query : queryMatches) {
+ tree.put(query, position);
+ }
+ String number = QueryFilteringUtil.digitsOnly(cursor.getString(Projections.PHONE_NUMBER));
+ Set<String> numberSubstrings = new ArraySet<>();
+ numberSubstrings.add(number);
+ for (int start = 0; start < number.length(); start++) {
+ numberSubstrings.add(number.substring(start, number.length()));
+ }
+ for (String substring : numberSubstrings) {
+ tree.put(substring, position);
+ }
+ }
+ return tree;
+ }
+
+ /**
+ * Returns a set containing:
+ *
+ * <ul>
+ * <li>The white space divided parts of phrase
+ * <li>The T9 representation of the white space divided parts of phrase
+ * <li>The T9 representation of the initials (i.e. first character of each part) of phrase
+ * </ul>
+ */
+ private static void addMatches(Context context, Set<String> existingMatches, String phrase) {
+ if (TextUtils.isEmpty(phrase)) {
+ return;
+ }
+ String initials = "";
+ phrase = phrase.toLowerCase(Locale.getDefault());
+ existingMatches.add(phrase);
+ for (String name : phrase.split("\\s")) {
+ if (TextUtils.isEmpty(name)) {
+ continue;
+ }
+ existingMatches.add(name);
+ existingMatches.add(QueryFilteringUtil.getT9Representation(name, context));
+ initials += name.charAt(0);
+ }
+ existingMatches.add(QueryFilteringUtil.getT9Representation(initials, context));
+ }
+
+ /**
* Filters out contacts that do not match the query.
*
* <p>The query can have at least 1 of 3 forms:
@@ -249,24 +315,14 @@ final class ContactFilterCursor implements Cursor {
query = "";
}
queryFilteredPositions.clear();
- query = query.toLowerCase();
- cursor.moveToPosition(-1);
-
- while (cursor.moveToNext()) {
- int position = cursor.getPosition();
- String number = cursor.getString(Projections.PHONE_NUMBER);
- String name = cursor.getString(Projections.DISPLAY_NAME);
- String companyName = cursor.getString(Projections.COMPANY_NAME);
- String nickName = cursor.getString(Projections.NICKNAME);
- if (TextUtils.isEmpty(query)
- || QueryFilteringUtil.nameMatchesT9Query(query, name, context)
- || QueryFilteringUtil.numberMatchesNumberQuery(query, number)
- || QueryFilteringUtil.nameContainsQuery(query, name)
- || QueryFilteringUtil.nameContainsQuery(query, companyName)
- || QueryFilteringUtil.nameContainsQuery(query, nickName)) {
- queryFilteredPositions.add(position);
+ if (TextUtils.isEmpty(query)) {
+ for (int i = 0; i < cursor.getCount(); i++) {
+ queryFilteredPositions.add(i);
}
+ } else {
+ queryFilteredPositions.addAll(contactTree.get(query.toLowerCase(Locale.getDefault())));
}
+ Collections.sort(queryFilteredPositions);
currentPosition = 0;
cursor.moveToFirst();
}
diff --git a/java/com/android/dialer/searchfragment/cp2/ContactTernarySearchTree.java b/java/com/android/dialer/searchfragment/cp2/ContactTernarySearchTree.java
new file mode 100644
index 000000000..88738e281
--- /dev/null
+++ b/java/com/android/dialer/searchfragment/cp2/ContactTernarySearchTree.java
@@ -0,0 +1,97 @@
+/*
+ * 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.searchfragment.cp2;
+
+import android.support.v4.util.ArraySet;
+import android.text.TextUtils;
+import java.util.Set;
+
+/** Ternary Search Tree for searching a list of contacts. */
+public class ContactTernarySearchTree {
+
+ private Node root;
+
+ /**
+ * Add {@code value} to all middle and end {@link Node#values} that correspond to {@code key}.
+ *
+ * <p>For example, if {@code key} were "FOO", {@code value} would be added to nodes "F", "O" and
+ * "O". But if the traversal required visiting {@link Node#left} or {@link Node#right}, {@code
+ * value} wouldn't be added to those nodes.
+ */
+ public void put(String key, int value) {
+ if (TextUtils.isEmpty(key)) {
+ return;
+ }
+ root = put(root, key, value, 0);
+ }
+
+ private Node put(Node node, String key, int value, int position) {
+ char c = key.charAt(position);
+ if (node == null) {
+ node = new Node();
+ node.key = c;
+ }
+ if (c < node.key) {
+ node.left = put(node.left, key, value, position);
+ } else if (c > node.key) {
+ node.right = put(node.right, key, value, position);
+ } else if (position < key.length() - 1) {
+ node.values.add(value);
+ node.mid = put(node.mid, key, value, position + 1);
+ } else {
+ node.values.add(value);
+ }
+ return node;
+ }
+
+ /** Returns true if {@code key} is contained in the trie. */
+ public boolean contains(String key) {
+ return !get(key).isEmpty();
+ }
+
+ /** Return value stored at Node (in this case, a set of integers). */
+ public Set<Integer> get(String key) {
+ Node x = get(root, key, 0);
+ return x == null ? new ArraySet<>() : x.values;
+ }
+
+ private Node get(Node node, String key, int position) {
+ if (node == null) {
+ return null;
+ }
+ char c = key.charAt(position);
+ if (c < node.key) {
+ return get(node.left, key, position);
+ } else if (c > node.key) {
+ return get(node.right, key, position);
+ } else if (position < key.length() - 1) {
+ return get(node.mid, key, position + 1);
+ } else {
+ return node;
+ }
+ }
+
+ /** Node in ternary search trie. Children are denoted as left, middle and right nodes. */
+ private static class Node {
+ private char key;
+ private final Set<Integer> values = new ArraySet<>();
+
+ private Node left;
+ private Node mid;
+ private Node right;
+ }
+}
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index 8306d37a6..93263ceb2 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -32,6 +32,7 @@ import android.support.annotation.VisibleForTesting;
import android.support.v13.app.FragmentCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
+import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -444,7 +445,9 @@ public final class NewSearchFragment extends Fragment
* the list of supported actions, see {@link SearchActionViewHolder.Action}.
*/
private List<Integer> getActions() {
- if (TextUtils.isEmpty(query) || query.length() == 1 || isRegularSearch()) {
+ boolean nonDialableQueryInRegularSearch =
+ isRegularSearch() && !PhoneNumberUtils.isGlobalPhoneNumber(query);
+ if (TextUtils.isEmpty(query) || query.length() == 1 || nonDialableQueryInRegularSearch) {
return Collections.emptyList();
}
diff --git a/java/com/android/dialer/telecom/TelecomUtil.java b/java/com/android/dialer/telecom/TelecomUtil.java
index 3bf9b4666..c79d9013d 100644
--- a/java/com/android/dialer/telecom/TelecomUtil.java
+++ b/java/com/android/dialer/telecom/TelecomUtil.java
@@ -33,9 +33,12 @@ import android.support.v4.content.ContextCompat;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.Pair;
import com.android.dialer.common.LogUtil;
+import com.google.common.base.Optional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -146,6 +149,24 @@ public abstract class TelecomUtil {
}
/**
+ * @return the {@link SubscriptionInfo} of the SIM if {@code phoneAccountHandle} corresponds to a
+ * valid SIM. Absent otherwise.
+ */
+ public static Optional<SubscriptionInfo> getSubscriptionInfo(
+ @NonNull Context context, @NonNull PhoneAccountHandle phoneAccountHandle) {
+ if (TextUtils.isEmpty(phoneAccountHandle.getId())) {
+ return Optional.absent();
+ }
+ SubscriptionManager subscriptionManager = context.getSystemService(SubscriptionManager.class);
+ for (SubscriptionInfo info : subscriptionManager.getActiveSubscriptionInfoList()) {
+ if (phoneAccountHandle.getId().startsWith(info.getIccId())) {
+ return Optional.of(info);
+ }
+ }
+ return Optional.absent();
+ }
+
+ /**
* Returns true if there is a dialer managed call in progress. Self managed calls starting from O
* are not included.
*/
diff --git a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
index 4629ce277..d5db60846 100644
--- a/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
+++ b/java/com/android/dialer/voicemail/listui/NewVoicemailMediaPlayerView.java
@@ -30,7 +30,7 @@ import android.support.v4.util.Pair;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
-import android.widget.Button;
+import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.dialer.common.Assert;
@@ -45,9 +45,10 @@ import com.android.dialer.voicemail.model.VoicemailEntry;
*/
public class NewVoicemailMediaPlayerView extends LinearLayout {
- private Button playButton;
- private Button speakerButton;
- private Button deleteButton;
+ private ImageButton playButton;
+ private ImageButton speakerButton;
+ private ImageButton phoneButton;
+ private ImageButton deleteButton;
private TextView totalDurationView;
private Uri voicemailUri;
private FragmentManager fragmentManager;
@@ -72,6 +73,7 @@ public class NewVoicemailMediaPlayerView extends LinearLayout {
private void initializeMediaPlayerButtonsAndViews() {
playButton = findViewById(R.id.playButton);
speakerButton = findViewById(R.id.speakerButton);
+ phoneButton = findViewById(R.id.phoneButton);
deleteButton = findViewById(R.id.deleteButton);
totalDurationView = findViewById(R.id.playback_seek_total_duration);
}
@@ -79,6 +81,7 @@ public class NewVoicemailMediaPlayerView extends LinearLayout {
private void setupListenersForMediaPlayerButtons() {
playButton.setOnClickListener(playButtonListener);
speakerButton.setOnClickListener(speakerButtonListener);
+ phoneButton.setOnClickListener(phoneButtonListener);
deleteButton.setOnClickListener(deleteButtonListener);
}
@@ -164,6 +167,17 @@ public class NewVoicemailMediaPlayerView extends LinearLayout {
}
};
+ private final View.OnClickListener phoneButtonListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ LogUtil.i(
+ "NewVoicemailMediaPlayer.phoneButtonListener",
+ "speaker request for voicemailUri: %s",
+ voicemailUri.toString());
+ }
+ };
+
private final View.OnClickListener deleteButtonListener =
new View.OnClickListener() {
@Override
diff --git a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_media_player_layout.xml b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_media_player_layout.xml
index e8e560059..07ce86a1d 100644
--- a/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_media_player_layout.xml
+++ b/java/com/android/dialer/voicemail/listui/res/layout/new_voicemail_media_player_layout.xml
@@ -67,22 +67,36 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="10dp"
- android:orientation="horizontal">
- <!-- TODO(a bug): Remove these buttons as this is a place holder for the Media Player -->
- <Button
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:weightSum="4">
+
+
+ <ImageButton
android:id="@+id/playButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Play"/>
- <Button
+ style="@style/voicemail_media_player_buttons"
+ android:layout_weight="1"
+ android:src="@drawable/quantum_ic_play_arrow_vd_theme_24"/>
+
+
+ <ImageButton
android:id="@+id/speakerButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Speaker"/>
- <Button
+ style="@style/voicemail_media_player_buttons"
+ android:layout_weight="1"
+ android:src="@drawable/quantum_ic_volume_up_vd_theme_24"/>
+
+
+ <ImageButton
+ android:id="@+id/phoneButton"
+ style="@style/voicemail_media_player_buttons"
+ android:layout_weight="1"
+ android:src="@drawable/quantum_ic_phone_vd_theme_24"/>
+
+ <ImageButton
android:id="@+id/deleteButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Delete"/>
+ style="@style/voicemail_media_player_buttons"
+ android:layout_weight="1"
+ android:src="@drawable/quantum_ic_delete_vd_theme_24"/>
+
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
index 6c062ae28..e37bc65fe 100644
--- a/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
+++ b/java/com/android/dialer/voicemail/listui/res/values/dimens.xml
@@ -29,6 +29,11 @@
<dimen name="voicemail_icon_size">16dp</dimen>
<dimen name="voicemail_playback_state_text_size">14sp</dimen>
+ <!-- TODO(uabdullah): Work with UX on this value to ensure proper spacing between
+ the seekbar and transcription -->
<dimen name="voicemail_media_player_padding_top">20dp</dimen>
<dimen name="voicemail_duration_size">14sp</dimen>
+ <!-- TODO(uabdullah): Work with UX on these values so that the touch target is not too small -->
+ <dimen name="voicemail_media_player_height">56dp</dimen>
+ <dimen name="voicemail_media_player_width">0dp</dimen>
</resources>
diff --git a/java/com/android/dialer/voicemail/listui/res/values/styles.xml b/java/com/android/dialer/voicemail/listui/res/values/styles.xml
new file mode 100644
index 000000000..aec46090a
--- /dev/null
+++ b/java/com/android/dialer/voicemail/listui/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="voicemail_media_player_buttons">
+ <item name="android:layout_width">@dimen/voicemail_media_player_width</item>
+ <item name="android:layout_height">@dimen/voicemail_media_player_height</item>
+ <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
index 2b496c0ca..aaa1e150d 100644
--- a/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
+++ b/java/com/android/dialer/voicemail/settings/VoicemailSettingsFragment.java
@@ -24,16 +24,22 @@ import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.provider.Settings;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
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.voicemail.VoicemailClient;
import com.android.voicemail.VoicemailClient.ActivationStateListener;
import com.android.voicemail.VoicemailComponent;
+import com.google.common.base.Optional;
/**
* Fragment for voicemail settings. Requires {@link VoicemailClient#PARAM_PHONE_ACCOUNT_HANDLE} set
@@ -45,6 +51,16 @@ public class VoicemailSettingsFragment extends PreferenceFragment
private static final String TAG = "VmSettingsActivity";
+ // Extras copied from com.android.phone.settings.VoicemailSettingsActivity,
+ // it does not recognize EXTRA_PHONE_ACCOUNT_HANDLE in O.
+ @VisibleForTesting
+ static final String SUB_ID_EXTRA =
+ "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionId";
+ // Extra on intent containing the label of a subscription.
+ @VisibleForTesting
+ static final String SUB_LABEL_EXTRA =
+ "com.android.phone.settings.SubscriptionInfoHelper.SubscriptionLabel";
+
@Nullable private PhoneAccountHandle phoneAccountHandle;
private VoicemailClient voicemailClient;
@@ -167,6 +183,19 @@ public class VoicemailSettingsFragment extends PreferenceFragment
advancedSettingsIntent.putExtra(TelephonyManager.EXTRA_HIDE_PUBLIC_SETTINGS, true);
advancedSettingsIntent.putExtra(
TelephonyManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle);
+
+ // (a bug): EXTRA_PHONE_ACCOUNT_HANDLE not implemented in telephony in O.
+ Optional<SubscriptionInfo> subscriptionInfo =
+ TelecomUtil.getSubscriptionInfo(getContext(), phoneAccountHandle);
+ if (subscriptionInfo.isPresent()) {
+ advancedSettingsIntent.putExtra(SUB_ID_EXTRA, subscriptionInfo.get().getSubscriptionId());
+ PhoneAccount phoneAccount =
+ getContext().getSystemService(TelecomManager.class).getPhoneAccount(phoneAccountHandle);
+ if (phoneAccount != null) {
+ advancedSettingsIntent.putExtra(SUB_LABEL_EXTRA, phoneAccount.getLabel());
+ }
+ }
+
advancedSettings.setIntent(advancedSettingsIntent);
voicemailChangePinPreference.setOnPreferenceClickListener(
new OnPreferenceClickListener() {
diff --git a/java/com/android/dialer/widget/FloatingActionButtonController.java b/java/com/android/dialer/widget/FloatingActionButtonController.java
index a0c4e6ddd..dde4d44ce 100644
--- a/java/com/android/dialer/widget/FloatingActionButtonController.java
+++ b/java/com/android/dialer/widget/FloatingActionButtonController.java
@@ -18,7 +18,7 @@ package com.android.dialer.widget;
import android.app.Activity;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
+import android.support.annotation.DrawableRes;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener;
import android.view.View;
@@ -39,6 +39,7 @@ public class FloatingActionButtonController {
private final int mFloatingActionButtonMarginRight;
private final FloatingActionButton mFab;
private final Interpolator mFabInterpolator;
+ private int mFabIconId = -1;
private int mScreenWidth;
public FloatingActionButtonController(Activity activity, FloatingActionButton fab) {
@@ -82,9 +83,12 @@ public class FloatingActionButtonController {
}
}
- public void changeIcon(Drawable icon, String description) {
- if (mFab.getDrawable() != icon || !mFab.getContentDescription().equals(description)) {
- mFab.setImageDrawable(icon);
+ public void changeIcon(@DrawableRes int iconId, String description) {
+ if (this.mFabIconId != iconId) {
+ mFab.setImageResource(iconId);
+ this.mFabIconId = iconId;
+ }
+ if (!mFab.getContentDescription().equals(description)) {
mFab.setContentDescription(description);
}
}
diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java
index cdab6b4f5..ed10ed0bb 100644
--- a/java/com/android/incallui/InCallActivity.java
+++ b/java/com/android/incallui/InCallActivity.java
@@ -16,6 +16,8 @@
package com.android.incallui;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.GradientDrawable;
@@ -34,6 +36,10 @@ import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.Toast;
+import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.ThreadUtil;
@@ -59,6 +65,8 @@ import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
import com.android.incallui.incall.protocol.InCallScreen;
import com.android.incallui.incall.protocol.InCallScreenDelegate;
import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
+import com.android.incallui.incalluilock.InCallUiLock;
+import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
import com.android.incallui.video.bindings.VideoBindings;
import com.android.incallui.video.protocol.VideoCallScreen;
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
@@ -76,8 +84,10 @@ public class InCallActivity extends TransactionSafeFragmentActivity
public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2;
- private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
+ private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
+ private static final String TAG_INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
+ private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
@@ -87,9 +97,11 @@ public class InCallActivity extends TransactionSafeFragmentActivity
private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
private final InCallActivityCommon common;
+ private InCallOrientationEventListener inCallOrientationEventListener;
private boolean didShowAnswerScreen;
private boolean didShowInCallScreen;
private boolean didShowVideoCallScreen;
+ private boolean dismissKeyguard;
private int[] backgroundDrawableColors;
private GradientDrawable backgroundDrawable;
private boolean isVisible;
@@ -138,6 +150,7 @@ public class InCallActivity extends TransactionSafeFragmentActivity
}
common.onCreate(icicle);
+ inCallOrientationEventListener = new InCallOrientationEventListener(this);
getWindow()
.getDecorView()
@@ -340,7 +353,21 @@ public class InCallActivity extends TransactionSafeFragmentActivity
}
public boolean isDialpadVisible() {
- return common.isDialpadVisible();
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ return dialpadFragment != null && dialpadFragment.isVisible();
+ }
+
+ /**
+ * Returns the {@link DialpadFragment} that's shown by this activity, or {@code null}
+ * TODO(a bug): Make this method private after InCallActivityCommon is deleted.
+ */
+ @Nullable
+ DialpadFragment getDialpadFragment() {
+ FragmentManager fragmentManager = getDialpadFragmentManager();
+ if (fragmentManager == null) {
+ return null;
+ }
+ return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
}
public void onForegroundCallChanged(DialerCall newForegroundCall) {
@@ -419,37 +446,109 @@ public class InCallActivity extends TransactionSafeFragmentActivity
}
public void dismissKeyguard(boolean dismiss) {
- common.dismissKeyguard(dismiss);
+ if (dismissKeyguard == dismiss) {
+ return;
+ }
+
+ dismissKeyguard = dismiss;
+ if (dismiss) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ } else {
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
}
public void showPostCharWaitDialog(String callId, String chars) {
common.showPostCharWaitDialog(callId, chars);
}
- public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
- common.maybeShowErrorDialogOnDisconnect(disconnectMessage);
+ public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
+ LogUtil.i(
+ "InCallActivity.showDialogOrToastForDisconnectedCall",
+ "disconnect cause: %s",
+ disconnectMessage);
+
+ if (disconnectMessage.dialog == null || isFinishing()) {
+ return;
+ }
+
+ dismissPendingDialogs();
+
+ // Show a toast if the app is in background when a dialog can't be visible.
+ if (!isVisible()) {
+ Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
+ .show();
+ return;
+ }
+
+ // Show the dialog.
+ common.setErrorDialog(disconnectMessage.dialog);
+ InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
+ disconnectMessage.dialog.setOnDismissListener(
+ dialogInterface -> {
+ lock.release();
+ onDialogDismissed();
+ });
+ disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ disconnectMessage.dialog.show();
+ }
+
+ private void onDialogDismissed() {
+ common.setErrorDialog(null);
+ CallList.getInstance().onErrorDialogDismissed();
}
public void dismissPendingDialogs() {
- if (isVisible) {
- LogUtil.i("InCallActivity.dismissPendingDialogs", "");
- common.dismissPendingDialogs();
- AnswerScreen answerScreen = getAnswerScreen();
- if (answerScreen != null) {
- answerScreen.dismissPendingDialogs();
- }
- needDismissPendingDialogs = false;
- } else {
- // The activity is not visible and onSaveInstanceState may have been called so defer the
- // dismissing action.
+ LogUtil.i("InCallActivity.dismissPendingDialogs", "");
+
+ if (!isVisible) {
+ // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
+ // been called.
LogUtil.i(
"InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
needDismissPendingDialogs = true;
+ return;
+ }
+
+ // Dismiss the error dialog
+ Dialog errorDialog = common.getErrorDialog();
+ if (errorDialog != null) {
+ errorDialog.dismiss();
+ common.setErrorDialog(null);
+ }
+
+ // Dismiss the phone account selection dialog
+ SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
+ common.getSelectPhoneAccountDialogFragment();
+ if (selectPhoneAccountDialogFragment != null) {
+ selectPhoneAccountDialogFragment.dismiss();
+ common.setSelectPhoneAccountDialogFragment(null);
+ }
+
+ // Dismiss the dialog for international call on WiFi
+ InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
+ (InternationalCallOnWifiDialogFragment)
+ getSupportFragmentManager().findFragmentByTag(TAG_INTERNATIONAL_CALL_ON_WIFI);
+ if (internationalCallOnWifiFragment != null) {
+ internationalCallOnWifiFragment.dismiss();
+ }
+
+ // Dismiss the answer screen
+ AnswerScreen answerScreen = getAnswerScreen();
+ if (answerScreen != null) {
+ answerScreen.dismissPendingDialogs();
}
+
+ needDismissPendingDialogs = false;
}
- private void enableInCallOrientationEventListener(boolean enable) {
- common.enableInCallOrientationEventListener(enable);
+ // TODO(a bug): Make this method private after InCallActivityCommon is deleted.
+ void enableInCallOrientationEventListener(boolean enable) {
+ if (enable) {
+ inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
+ } else {
+ inCallOrientationEventListener.disable();
+ }
}
public void setExcludeFromRecents(boolean exclude) {
@@ -515,17 +614,67 @@ public class InCallActivity extends TransactionSafeFragmentActivity
Trace.endSection();
}
- public void onWiFiToLteHandover(DialerCall call) {
- common.showWifiToLteHandoverToast(call);
- }
+ public void showToastForWiFiToLteHandover(DialerCall call) {
+ if (call.hasShownWiFiToLteHandoverToast()) {
+ return;
+ }
- public void onHandoverToWifiFailed(DialerCall call) {
- common.showWifiFailedDialog(call);
+ Toast.makeText(this, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG).show();
+ call.setHasShownWiFiToLteHandoverToast();
}
- public void onInternationalCallOnWifi(@NonNull DialerCall call) {
- LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
- common.showInternationalCallOnWifiDialog(call);
+ public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
+ if (call.showWifiHandoverAlertAsToast()) {
+ Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ dismissPendingDialogs();
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
+
+ // This allows us to use the theme of the dialog instead of the activity
+ View dialogCheckBoxView =
+ View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
+ CheckBox wifiHandoverFailureCheckbox =
+ (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
+ wifiHandoverFailureCheckbox.setChecked(false);
+
+ InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
+ Dialog errorDialog =
+ builder
+ .setView(dialogCheckBoxView)
+ .setMessage(R.string.video_call_lte_to_wifi_failed_message)
+ .setOnCancelListener(dialogInterface -> onDialogDismissed())
+ .setPositiveButton(
+ android.R.string.ok,
+ (dialogInterface, id) -> {
+ call.setDoNotShowDialogForHandoffToWifiFailure(
+ wifiHandoverFailureCheckbox.isChecked());
+ dialogInterface.cancel();
+ onDialogDismissed();
+ })
+ .setOnDismissListener(dialogInterface -> lock.release())
+ .create();
+
+ common.setErrorDialog(errorDialog);
+ errorDialog.show();
+ }
+
+ public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
+ if (!InternationalCallOnWifiDialogFragment.shouldShow(this)) {
+ LogUtil.i(
+ "InCallActivity.showDialogForInternationalCallOnWifi",
+ "InternationalCallOnWifiDialogFragment.shouldShow returned false");
+ return;
+ }
+
+ InternationalCallOnWifiDialogFragment fragment =
+ InternationalCallOnWifiDialogFragment.newInstance(
+ call.getId(), common.getCallbackForInternationalCallOnWifiDialog());
+ fragment.show(getSupportFragmentManager(), TAG_INTERNATIONAL_CALL_ON_WIFI);
}
@Override
diff --git a/java/com/android/incallui/InCallActivityCommon.java b/java/com/android/incallui/InCallActivityCommon.java
index 5a5d770d0..8f82295ed 100644
--- a/java/com/android/incallui/InCallActivityCommon.java
+++ b/java/com/android/incallui/InCallActivityCommon.java
@@ -19,12 +19,8 @@ package com.android.incallui;
import android.app.ActivityManager;
import android.app.ActivityManager.AppTask;
import android.app.ActivityManager.TaskDescription;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.app.KeyguardManager;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
-import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,8 +42,6 @@ import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
-import android.widget.CheckBox;
-import android.widget.Toast;
import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
import com.android.dialer.animation.AnimUtils;
@@ -63,8 +57,6 @@ import com.android.incallui.call.CallList;
import com.android.incallui.call.DialerCall;
import com.android.incallui.call.DialerCall.State;
import com.android.incallui.call.TelecomAdapter;
-import com.android.incallui.disconnectdialog.DisconnectMessage;
-import com.android.incallui.incalluilock.InCallUiLock;
import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment.Callback;
import com.google.common.base.Optional;
@@ -102,13 +94,11 @@ public class InCallActivityCommon {
private static Optional<Integer> audioRouteForTesting = Optional.absent();
private final InCallActivity inCallActivity;
- private boolean dismissKeyguard;
private boolean showPostCharWaitDialogOnResume;
private String showPostCharWaitDialogCallId;
private String showPostCharWaitDialogChars;
- private Dialog dialog;
+ private Dialog errorDialog;
private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
- private InCallOrientationEventListener inCallOrientationEventListener;
private Animation dialpadSlideInAnimation;
private Animation dialpadSlideOutAnimation;
private boolean animateDialpadOnShow;
@@ -243,14 +233,12 @@ public class InCallActivityCommon {
"InCallActivityCommon.onCreate", "international fragment exists attaching callback");
existingInternationalFragment.setCallback(internationalCallOnWifiCallback);
}
-
- inCallOrientationEventListener = new InCallOrientationEventListener(inCallActivity);
}
public void onSaveInstanceState(Bundle out) {
// TODO: The dialpad fragment should handle this as part of its own state
- out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible());
- DialpadFragment dialpadFragment = getDialpadFragment();
+ out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, inCallActivity.isDialpadVisible());
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment != null) {
out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText());
}
@@ -260,14 +248,11 @@ public class InCallActivityCommon {
Trace.beginSection("InCallActivityCommon.onStart");
// setting activity should be last thing in setup process
InCallPresenter.getInstance().setActivity(inCallActivity);
- enableInCallOrientationEventListener(
+ inCallActivity.enableInCallOrientationEventListener(
inCallActivity.getRequestedOrientation()
== InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
InCallPresenter.getInstance().onActivityStarted();
- if (!isRecreating) {
- InCallPresenter.getInstance().onUiShowing(true);
- }
Trace.endSection();
}
@@ -279,6 +264,7 @@ public class InCallActivityCommon {
"InCallPresenter is ready for tear down, not sending updates");
} else {
updateTaskDescription();
+ InCallPresenter.getInstance().onUiShowing(true);
}
// If there is a pending request to show or hide the dialpad, handle that now.
@@ -291,20 +277,20 @@ public class InCallActivityCommon {
inCallActivity.showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
animateDialpadOnShow = false;
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment != null) {
dialpadFragment.setDtmfText(dtmfTextToPreopulate);
dtmfTextToPreopulate = null;
}
} else {
LogUtil.i("InCallActivityCommon.onResume", "force hide dialpad");
- if (getDialpadFragment() != null) {
+ if (inCallActivity.getDialpadFragment() != null) {
inCallActivity.showDialpadFragment(false /* show */, false /* animate */);
}
}
showDialpadRequest = DIALPAD_REQUEST_NONE;
}
- updateNavigationBar(isDialpadVisible());
+ updateNavigationBar(inCallActivity.isDialpadVisible());
if (showPostCharWaitDialogOnResume) {
showPostCharWaitDialog(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
@@ -319,10 +305,15 @@ public class InCallActivityCommon {
// onPause is guaranteed to be called when the InCallActivity goes
// in the background.
public void onPause() {
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment != null) {
dialpadFragment.onDialerKeyUp(null);
}
+
+ InCallPresenter.getInstance().onUiShowing(false);
+ if (inCallActivity.isFinishing()) {
+ InCallPresenter.getInstance().unsetActivity(inCallActivity);
+ }
}
public void onStop() {
@@ -338,18 +329,14 @@ public class InCallActivityCommon {
}
}
- enableInCallOrientationEventListener(false);
+ inCallActivity.enableInCallOrientationEventListener(false);
InCallPresenter.getInstance().updateIsChangingConfigurations();
InCallPresenter.getInstance().onActivityStopped();
if (!isRecreating) {
- InCallPresenter.getInstance().onUiShowing(false);
- if (dialog != null) {
- dialog.dismiss();
+ if (errorDialog != null) {
+ errorDialog.dismiss();
}
}
- if (inCallActivity.isFinishing()) {
- InCallPresenter.getInstance().unsetActivity(inCallActivity);
- }
}
public void onDestroy() {
@@ -394,7 +381,7 @@ public class InCallActivityCommon {
return true;
}
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment != null && dialpadFragment.isVisible()) {
inCallActivity.showDialpadFragment(false /* show */, true /* animate */);
return true;
@@ -412,7 +399,7 @@ public class InCallActivityCommon {
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
// push input to the dialer.
if (dialpadFragment != null
&& (dialpadFragment.isVisible())
@@ -517,7 +504,7 @@ public class InCallActivityCommon {
// As soon as the user starts typing valid dialable keys on the
// keyboard (presumably to type DTMF tones) we start passing the
// key events to the DTMFDialer's onDialerKeyDown.
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment != null && dialpadFragment.isVisible()) {
return dialpadFragment.onDialerKeyDown(event);
}
@@ -525,18 +512,6 @@ public class InCallActivityCommon {
return false;
}
- public void dismissKeyguard(boolean dismiss) {
- if (dismissKeyguard == dismiss) {
- return;
- }
- dismissKeyguard = dismiss;
- if (dismiss) {
- inCallActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- } else {
- inCallActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
- }
-
public void showPostCharWaitDialog(String callId, String chars) {
if (inCallActivity.isVisible()) {
PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
@@ -552,19 +527,6 @@ public class InCallActivityCommon {
}
}
- public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
- LogUtil.i(
- "InCallActivityCommon.maybeShowErrorDialogOnDisconnect",
- "disconnect cause: %s",
- disconnectMessage);
-
- if (!inCallActivity.isFinishing()) {
- if (disconnectMessage.dialog != null) {
- showErrorDialog(disconnectMessage.dialog, disconnectMessage.toastMessage);
- }
- }
- }
-
/**
* When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
* be shown on launch.
@@ -586,67 +548,6 @@ public class InCallActivityCommon {
}
}
- void dismissPendingDialogs() {
- if (dialog != null) {
- dialog.dismiss();
- dialog = null;
- }
- if (selectPhoneAccountDialogFragment != null) {
- selectPhoneAccountDialogFragment.dismiss();
- selectPhoneAccountDialogFragment = null;
- }
-
- InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
- (InternationalCallOnWifiDialogFragment)
- inCallActivity
- .getSupportFragmentManager()
- .findFragmentByTag(TAG_INTERNATIONAL_CALL_ON_WIFI);
- if (internationalCallOnWifiFragment != null) {
- LogUtil.i(
- "InCallActivityCommon.dismissPendingDialogs",
- "dismissing InternationalCallOnWifiDialogFragment");
- internationalCallOnWifiFragment.dismiss();
- }
- }
-
- private void showErrorDialog(Dialog dialog, CharSequence message) {
- LogUtil.i("InCallActivityCommon.showErrorDialog", "message: %s", message);
- inCallActivity.dismissPendingDialogs();
-
- // Show toast if apps is in background when dialog won't be visible.
- if (!inCallActivity.isVisible()) {
- Toast.makeText(inCallActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show();
- return;
- }
-
- this.dialog = dialog;
- InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
- dialog.setOnDismissListener(
- new OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- LogUtil.i("InCallActivityCommon.showErrorDialog", "dialog dismissed");
- lock.release();
- onDialogDismissed();
- }
- });
- dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
- dialog.show();
- }
-
- private void onDialogDismissed() {
- dialog = null;
- CallList.getInstance().onErrorDialogDismissed();
- }
-
- public void enableInCallOrientationEventListener(boolean enable) {
- if (enable) {
- inCallOrientationEventListener.enable(true);
- } else {
- inCallOrientationEventListener.disable();
- }
- }
-
public void setExcludeFromRecents(boolean exclude) {
List<AppTask> tasks = inCallActivity.getSystemService(ActivityManager.class).getAppTasks();
int taskId = inCallActivity.getTaskId();
@@ -665,83 +566,6 @@ public class InCallActivityCommon {
}
}
- void showInternationalCallOnWifiDialog(@NonNull DialerCall call) {
- LogUtil.enterBlock("InCallActivityCommon.showInternationalCallOnWifiDialog");
- if (!InternationalCallOnWifiDialogFragment.shouldShow(inCallActivity)) {
- LogUtil.i(
- "InCallActivityCommon.showInternationalCallOnWifiDialog",
- "InternationalCallOnWifiDialogFragment.shouldShow returned false");
- return;
- }
-
- InternationalCallOnWifiDialogFragment fragment =
- InternationalCallOnWifiDialogFragment.newInstance(
- call.getId(), internationalCallOnWifiCallback);
- fragment.show(inCallActivity.getSupportFragmentManager(), TAG_INTERNATIONAL_CALL_ON_WIFI);
- }
-
- public void showWifiToLteHandoverToast(DialerCall call) {
- if (call.hasShownWiFiToLteHandoverToast()) {
- return;
- }
- Toast.makeText(
- inCallActivity, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG)
- .show();
- call.setHasShownWiFiToLteHandoverToast();
- }
-
- public void showWifiFailedDialog(final DialerCall call) {
- if (call.showWifiHandoverAlertAsToast()) {
- LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as toast");
- Toast.makeText(
- inCallActivity, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
- .show();
- return;
- }
-
- dismissPendingDialogs();
-
- AlertDialog.Builder builder =
- new AlertDialog.Builder(inCallActivity)
- .setTitle(R.string.video_call_lte_to_wifi_failed_title);
-
- // This allows us to use the theme of the dialog instead of the activity
- View dialogCheckBoxView =
- View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null);
- final CheckBox wifiHandoverFailureCheckbox =
- (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
- wifiHandoverFailureCheckbox.setChecked(false);
-
- InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
- dialog =
- builder
- .setView(dialogCheckBoxView)
- .setMessage(R.string.video_call_lte_to_wifi_failed_message)
- .setOnCancelListener(
- new OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- onDialogDismissed();
- }
- })
- .setPositiveButton(
- android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- call.setDoNotShowDialogForHandoffToWifiFailure(
- wifiHandoverFailureCheckbox.isChecked());
- dialog.cancel();
- onDialogDismissed();
- }
- })
- .setOnDismissListener((dialog) -> lock.release())
- .create();
-
- LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as dialog");
- dialog.show();
- }
-
void updateNavigationBar(boolean isDialpadVisible) {
if (!ActivityCompat.isInMultiWindowMode(inCallActivity)) {
View navigationBarBackground =
@@ -754,7 +578,7 @@ public class InCallActivityCommon {
public boolean showDialpadFragment(boolean show, boolean animate) {
// If the dialpad is already visible, don't animate in. If it's gone, don't animate out.
- boolean isDialpadVisible = isDialpadVisible();
+ boolean isDialpadVisible = inCallActivity.isDialpadVisible();
LogUtil.i(
"InCallActivityCommon.showDialpadFragment",
"show: %b, animate: %b, " + "isDialpadVisible: %b",
@@ -783,9 +607,10 @@ public class InCallActivityCommon {
} else {
if (show) {
performShowDialpadFragment(dialpadFragmentManager);
- getDialpadFragment().animateShowDialpad();
+ inCallActivity.getDialpadFragment().animateShowDialpad();
}
- getDialpadFragment()
+ inCallActivity
+ .getDialpadFragment()
.getView()
.startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
}
@@ -800,7 +625,7 @@ public class InCallActivityCommon {
private void performShowDialpadFragment(@NonNull FragmentManager dialpadFragmentManager) {
FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
- DialpadFragment dialpadFragment = getDialpadFragment();
+ DialpadFragment dialpadFragment = inCallActivity.getDialpadFragment();
if (dialpadFragment == null) {
transaction.add(
inCallActivity.getDialpadContainerId(), new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
@@ -833,21 +658,6 @@ public class InCallActivityCommon {
updateNavigationBar(false /* isDialpadVisible */);
}
- public boolean isDialpadVisible() {
- DialpadFragment dialpadFragment = getDialpadFragment();
- return dialpadFragment != null && dialpadFragment.isVisible();
- }
-
- /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
- @Nullable
- private DialpadFragment getDialpadFragment() {
- FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
- if (fragmentManager == null) {
- return null;
- }
- return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
- }
-
public void updateTaskDescription() {
Resources resources = inCallActivity.getResources();
int color;
@@ -864,10 +674,6 @@ public class InCallActivityCommon {
inCallActivity.setTaskDescription(td);
}
- public boolean hasPendingDialogs() {
- return dialog != null;
- }
-
private void internalResolveIntent(Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
return;
@@ -902,7 +708,7 @@ public class InCallActivityCommon {
outgoingCall.disconnect();
}
- dismissKeyguard(true);
+ inCallActivity.dismissKeyguard(true);
}
boolean didShowAccountSelectionDialog = maybeShowAccountSelectionDialog();
@@ -937,4 +743,37 @@ public class InCallActivityCommon {
inCallActivity.getFragmentManager(), TAG_SELECT_ACCOUNT_FRAGMENT);
return true;
}
+
+ /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */
+ @Deprecated
+ @Nullable
+ Dialog getErrorDialog() {
+ return errorDialog;
+ }
+
+ /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */
+ @Deprecated
+ void setErrorDialog(@Nullable Dialog errorDialog) {
+ this.errorDialog = errorDialog;
+ }
+
+ /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */
+ @Deprecated
+ @Nullable
+ SelectPhoneAccountDialogFragment getSelectPhoneAccountDialogFragment() {
+ return selectPhoneAccountDialogFragment;
+ }
+
+ /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */
+ @Deprecated
+ void setSelectPhoneAccountDialogFragment(
+ @Nullable SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment) {
+ this.selectPhoneAccountDialogFragment = selectPhoneAccountDialogFragment;
+ }
+
+ /** @deprecated Only for temporary use during the deprecation of {@link InCallActivityCommon} */
+ @Deprecated
+ InternationalCallOnWifiDialogFragment.Callback getCallbackForInternationalCallOnWifiDialog() {
+ return internationalCallOnWifiCallback;
+ }
}
diff --git a/java/com/android/incallui/InCallOrientationEventListener.java b/java/com/android/incallui/InCallOrientationEventListener.java
index e6b0bc027..8aae6fb37 100644
--- a/java/com/android/incallui/InCallOrientationEventListener.java
+++ b/java/com/android/incallui/InCallOrientationEventListener.java
@@ -126,12 +126,13 @@ public class InCallOrientationEventListener extends OrientationEventListener {
}
/**
- * Enables the OrientationEventListener and notifies listeners of current orientation if notify
- * flag is true
+ * Enables the OrientationEventListener and optionally notifies listeners of the current
+ * orientation.
*
- * @param notify true or false. Notify device orientation changed if true.
+ * @param notifyDeviceOrientationChange Whether to notify listeners that the device orientation is
+ * changed.
*/
- public void enable(boolean notify) {
+ public void enable(boolean notifyDeviceOrientationChange) {
if (mEnabled) {
Log.v(this, "enable: Orientation listener is already enabled. Ignoring...");
return;
@@ -139,15 +140,15 @@ public class InCallOrientationEventListener extends OrientationEventListener {
super.enable();
mEnabled = true;
- if (notify) {
+ if (notifyDeviceOrientationChange) {
InCallPresenter.getInstance().onDeviceOrientationChange(sCurrentOrientation);
}
}
- /** Enables the OrientationEventListener with notify flag defaulting to false. */
+ /** Enables the OrientationEventListener. */
@Override
public void enable() {
- enable(false);
+ enable(false /* notifyDeviceOrientationChange */);
}
/** Disables the OrientationEventListener. */
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 6c1c13027..3ba2ccff6 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -88,8 +88,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class InCallPresenter implements CallList.Listener, AudioModeProvider.AudioModeListener {
private static final String PIXEL2017_SYSTEM_FEATURE =
"com.google.android.feature.PIXEL_2017_EXPERIENCE";
- private static final String EXTRA_FIRST_TIME_SHOWN =
- "com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
@@ -215,14 +213,7 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
}
}
};
- /**
- * Is true when the activity has been previously started. Some code needs to know not just if the
- * activity is currently up, but if it had been previously shown in foreground for this in-call
- * session (e.g., StatusBarNotifier). This gets reset when the session ends in the tear-down
- * method.
- */
- private boolean mIsActivityPreviouslyStarted = false;
-
+
/** Whether or not InCallService is bound to Telecom. */
private boolean mServiceBound = false;
@@ -476,7 +467,7 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
// By the time the UI finally comes up, the call may already be disconnected.
// If that's the case, we may need to show an error dialog.
if (mCallList != null && mCallList.getDisconnectedCall() != null) {
- maybeShowErrorDialogOnDisconnect(mCallList.getDisconnectedCall());
+ showDialogOrToastForDisconnectedCall(mCallList.getDisconnectedCall());
}
// When the UI comes up, we need to first check the in-call state.
@@ -690,14 +681,14 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
@Override
public void onWiFiToLteHandover(DialerCall call) {
if (mInCallActivity != null) {
- mInCallActivity.onWiFiToLteHandover(call);
+ mInCallActivity.showToastForWiFiToLteHandover(call);
}
}
@Override
public void onHandoverToWifiFailed(DialerCall call) {
if (mInCallActivity != null) {
- mInCallActivity.onHandoverToWifiFailed(call);
+ mInCallActivity.showDialogOrToastForWifiHandoverFailure(call);
}
}
@@ -705,7 +696,7 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
public void onInternationalCallOnWifi(@NonNull DialerCall call) {
LogUtil.enterBlock("InCallPresenter.onInternationalCallOnWifi");
if (mInCallActivity != null) {
- mInCallActivity.onInternationalCallOnWifi(call);
+ mInCallActivity.showDialogForInternationalCallOnWifi(call);
}
}
@@ -841,7 +832,7 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
*/
@Override
public void onDisconnect(DialerCall call) {
- maybeShowErrorDialogOnDisconnect(call);
+ showDialogOrToastForDisconnectedCall(call);
// We need to do the run the same code as onCallListChange.
onCallListChange(mCallList);
@@ -1052,22 +1043,7 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
mProximitySensor.onInCallShowing(showing);
}
- Intent broadcastIntent = Bindings.get(mContext).getUiReadyBroadcastIntent(mContext);
- if (broadcastIntent != null) {
- broadcastIntent.putExtra(EXTRA_FIRST_TIME_SHOWN, !mIsActivityPreviouslyStarted);
-
- if (showing) {
- LogUtil.d("InCallPresenter.onUiShowing", "Sending sticky broadcast: ", broadcastIntent);
- mContext.sendStickyBroadcast(broadcastIntent);
- } else {
- LogUtil.d("InCallPresenter.onUiShowing", "Removing sticky broadcast: ", broadcastIntent);
- mContext.removeStickyBroadcast(broadcastIntent);
- }
- }
-
- if (showing) {
- mIsActivityPreviouslyStarted = true;
- } else {
+ if (!showing) {
updateIsChangingConfigurations();
}
@@ -1265,19 +1241,19 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
}
}
- /**
- * For some disconnected causes, we show a dialog. This calls into the activity to show the dialog
- * if appropriate for the call.
- */
- private void maybeShowErrorDialogOnDisconnect(DialerCall call) {
+ /** Instruct the in-call activity to show an error dialog or toast for a disconnected call. */
+ private void showDialogOrToastForDisconnectedCall(DialerCall call) {
+ if (!isActivityStarted() || call.getState() != DialerCall.State.DISCONNECTED) {
+ return;
+ }
+
// For newly disconnected calls, we may want to show a dialog on specific error conditions
- if (isActivityStarted() && call.getState() == DialerCall.State.DISCONNECTED) {
- if (call.getAccountHandle() == null && !call.isConferenceCall()) {
- setDisconnectCauseForMissingAccounts(call);
- }
- mInCallActivity.maybeShowErrorDialogOnDisconnect(
- new DisconnectMessage(mInCallActivity, call));
+ if (call.getAccountHandle() == null && !call.isConferenceCall()) {
+ setDisconnectCauseForMissingAccounts(call);
}
+
+ mInCallActivity.showDialogOrToastForDisconnectedCall(
+ new DisconnectMessage(mInCallActivity, call));
}
/**
@@ -1449,7 +1425,6 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud
cleanupSurfaces();
- mIsActivityPreviouslyStarted = false;
mIsChangingConfigurations = false;
// blow away stale contact info so that we get fresh data on
diff --git a/java/com/android/incallui/bindings/InCallUiBindings.java b/java/com/android/incallui/bindings/InCallUiBindings.java
index 5c6aef4be..c15b68de9 100644
--- a/java/com/android/incallui/bindings/InCallUiBindings.java
+++ b/java/com/android/incallui/bindings/InCallUiBindings.java
@@ -26,10 +26,6 @@ public interface InCallUiBindings {
@Nullable
PhoneNumberService newPhoneNumberService(Context context);
- /** @return An {@link Intent} to be broadcast when the InCallUI is visible. */
- @Nullable
- Intent getUiReadyBroadcastIntent(Context context);
-
/**
* @return An {@link Intent} to be broadcast when the call state button in the InCallUI is touched
* while in a call.
diff --git a/java/com/android/incallui/bindings/InCallUiBindingsStub.java b/java/com/android/incallui/bindings/InCallUiBindingsStub.java
index 3a005b0fb..3a9e1dc52 100644
--- a/java/com/android/incallui/bindings/InCallUiBindingsStub.java
+++ b/java/com/android/incallui/bindings/InCallUiBindingsStub.java
@@ -31,12 +31,6 @@ public class InCallUiBindingsStub implements InCallUiBindings {
@Override
@Nullable
- public Intent getUiReadyBroadcastIntent(Context context) {
- return null;
- }
-
- @Override
- @Nullable
public Intent getCallStateButtonBroadcastIntent(Context context) {
return null;
}
diff --git a/java/com/android/incallui/incall/impl/ButtonController.java b/java/com/android/incallui/incall/impl/ButtonController.java
index cefbd723b..5e37a492b 100644
--- a/java/com/android/incallui/incall/impl/ButtonController.java
+++ b/java/com/android/incallui/incall/impl/ButtonController.java
@@ -16,6 +16,7 @@
package com.android.incallui.incall.impl;
+import android.graphics.drawable.AnimationDrawable;
import android.support.annotation.CallSuper;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
@@ -569,11 +570,14 @@ interface ButtonController {
InCallButtonIds.BUTTON_SWAP_SIM,
R.string.incall_content_description_swap_sim,
R.string.incall_label_swap_sim,
- R.drawable.quantum_ic_swap_calls_white_36);
+ R.drawable.ic_sim_change_white);
}
@Override
public void onClick(View view) {
+ AnimationDrawable drawable = (AnimationDrawable) button.getIconDrawable();
+ drawable.stop(); // animation is one shot, stop it so it can be started again.
+ drawable.start();
delegate.swapSimClicked();
}
}
diff --git a/java/com/android/incallui/incall/impl/CheckableLabeledButton.java b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java
index 325c3a92a..ca018acc2 100644
--- a/java/com/android/incallui/incall/impl/CheckableLabeledButton.java
+++ b/java/com/android/incallui/incall/impl/CheckableLabeledButton.java
@@ -47,6 +47,7 @@ public class CheckableLabeledButton extends LinearLayout implements Checkable {
private boolean isChecked;
private OnCheckedChangeListener onCheckedChangeListener;
private ImageView iconView;
+ @DrawableRes private int iconResource = 0;
private TextView labelView;
private Drawable background;
private Drawable backgroundMore;
@@ -135,8 +136,15 @@ public class CheckableLabeledButton extends LinearLayout implements Checkable {
new int[] {color, Color.WHITE}));
}
+ public Drawable getIconDrawable() {
+ return iconView.getDrawable();
+ }
+
public void setIconDrawable(@DrawableRes int drawableRes) {
- iconView.setImageResource(drawableRes);
+ if (iconResource != drawableRes) {
+ iconView.setImageResource(drawableRes);
+ iconResource = drawableRes;
+ }
}
public void setLabelText(@StringRes int stringRes) {
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_00.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_00.png
new file mode 100644
index 000000000..4c8b33f95
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_00.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_01.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_01.png
new file mode 100644
index 000000000..910177920
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_01.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_02.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_02.png
new file mode 100644
index 000000000..92a27eed2
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_02.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_03.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_03.png
new file mode 100644
index 000000000..484058f22
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_03.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_04.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_04.png
new file mode 100644
index 000000000..348ae927a
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_04.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_05.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_05.png
new file mode 100644
index 000000000..011915a93
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_05.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_06.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_06.png
new file mode 100644
index 000000000..c1cc0d6d7
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_06.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_07.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_07.png
new file mode 100644
index 000000000..75233db73
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_07.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_08.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_08.png
new file mode 100644
index 000000000..2918e1a4b
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_08.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_09.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_09.png
new file mode 100644
index 000000000..7e927674c
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_09.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_10.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_10.png
new file mode 100644
index 000000000..008931431
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_10.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_11.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_11.png
new file mode 100644
index 000000000..f66a6b669
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_11.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_12.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_12.png
new file mode 100644
index 000000000..9303d9596
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_12.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_13.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_13.png
new file mode 100644
index 000000000..d2bef6e77
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_13.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_14.png b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_14.png
new file mode 100644
index 000000000..a5434ecdc
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable-xxxhdpi/ic_sim_change_white_14.png
Binary files differ
diff --git a/java/com/android/incallui/incall/impl/res/drawable/ic_sim_change_white.xml b/java/com/android/incallui/incall/impl/res/drawable/ic_sim_change_white.xml
new file mode 100644
index 000000000..00b1b7ace
--- /dev/null
+++ b/java/com/android/incallui/incall/impl/res/drawable/ic_sim_change_white.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/ic_sim_change_white"
+ android:oneshot="true">
+ <item
+ android:drawable="@drawable/ic_sim_change_white_00"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_01"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_02"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_03"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_04"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_05"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_06"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_07"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_08"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_09"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_10"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_11"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_12"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_13"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_14"
+ android:duration="33"/>
+ <item
+ android:drawable="@drawable/ic_sim_change_white_00"
+ android:duration="33"/>
+</animation-list> \ No newline at end of file