diff options
author | Eric Erfanian <erfanian@google.com> | 2018-04-26 15:34:21 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-04-26 15:34:21 +0000 |
commit | 52a161148b057ebd4b0c86228b82cba305f1102d (patch) | |
tree | 35906c59b5daccce7f7dacd2ca8a01aaa3dcec5c | |
parent | 43ed0dc0666e9d2e2837a2019e2765f10f43dff8 (diff) | |
parent | aa9d670a4f076e52418cd5e404435c524713278e (diff) |
Merge changes I6883ce15,Ife5e6bad,If5a40a97,I36916560
* changes:
Persist contacts pinned positions in speed dial.
Fix crash when saving transcript is called multiple times.
Show post char dialog without InCallActivity.
UI tweak to RTT chat input box.
15 files changed, 303 insertions, 59 deletions
diff --git a/java/com/android/dialer/speeddial/SpeedDialAdapter.java b/java/com/android/dialer/speeddial/SpeedDialAdapter.java index 8a37e97dd..a382b1a6b 100644 --- a/java/com/android/dialer/speeddial/SpeedDialAdapter.java +++ b/java/com/android/dialer/speeddial/SpeedDialAdapter.java @@ -34,10 +34,10 @@ import com.android.dialer.speeddial.HeaderViewHolder.SpeedDialHeaderListener; import com.android.dialer.speeddial.SuggestionViewHolder.SuggestedContactsListener; import com.android.dialer.speeddial.draghelper.SpeedDialItemTouchHelperCallback.ItemTouchHelperAdapter; import com.android.dialer.speeddial.loader.SpeedDialUiItem; +import com.google.common.collect.ImmutableList; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -146,7 +146,13 @@ public final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.Vi public void setSpeedDialUiItems(List<SpeedDialUiItem> immutableSpeedDialUiItems) { speedDialUiItems = new ArrayList<>(); speedDialUiItems.addAll(immutableSpeedDialUiItems); - speedDialUiItems.sort((o1, o2) -> Boolean.compare(o2.isStarred(), o1.isStarred())); + speedDialUiItems.sort( + (o1, o2) -> { + if (o1.isStarred() && o2.isStarred()) { + return Integer.compare(o1.pinnedPosition().or(-1), o2.pinnedPosition().or(-1)); + } + return Boolean.compare(o2.isStarred(), o1.isStarred()); + }); positionToRowTypeMap.clear(); if (speedDialUiItems.isEmpty()) { return; @@ -168,6 +174,13 @@ public final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.Vi } } + public ImmutableList<SpeedDialUiItem> getSpeedDialUiItems() { + if (speedDialUiItems == null || speedDialUiItems.isEmpty()) { + return ImmutableList.of(); + } + return ImmutableList.copyOf(speedDialUiItems); + } + public SpanSizeLookup getSpanSizeLookup() { return new SpanSizeLookup() { @Override @@ -189,16 +202,9 @@ public final class SpeedDialAdapter extends RecyclerView.Adapter<RecyclerView.Vi @Override public void onItemMove(int fromPosition, int toPosition) { - if (fromPosition < toPosition) { - for (int i = fromPosition; i < toPosition && i < speedDialUiItems.size() - 1; i++) { - Collections.swap(speedDialUiItems, i, i + 1); - } - } else { - for (int i = fromPosition - 1; i > toPosition; i--) { - Collections.swap(speedDialUiItems, i, i - 1); - } - } - // TODO(calderwoodra): store pinned positions + // fromPosition/toPosition correspond to adapter position, which is off by 1 from the list + // position b/c of the favorites header. So subtract 1 here. + speedDialUiItems.add(toPosition - 1, speedDialUiItems.remove(fromPosition - 1)); notifyItemMoved(fromPosition, toPosition); } diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java index b74c06239..b76db1cf3 100644 --- a/java/com/android/dialer/speeddial/SpeedDialFragment.java +++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java @@ -36,6 +36,7 @@ import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.common.FragmentUtils; import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DefaultFutureCallback; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.SupportUiListener; import com.android.dialer.constants.ActivityRequestCodes; @@ -54,6 +55,7 @@ import com.android.dialer.speeddial.loader.SpeedDialUiItem; import com.android.dialer.speeddial.loader.UiItemLoaderComponent; import com.android.dialer.util.IntentUtil; import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.Futures; /** * Fragment for displaying: @@ -143,7 +145,7 @@ public class SpeedDialFragment extends Fragment { speedDialLoaderListener.listen( getContext(), - UiItemLoaderComponent.get(getContext()).speedDialUiItemLoader().loadSpeedDialUiItems(), + UiItemLoaderComponent.get(getContext()).speedDialUiItemMutator().loadSpeedDialUiItems(), this::onSpeedDialUiItemListLoaded, throwable -> { throw new RuntimeException(throwable); @@ -158,7 +160,7 @@ public class SpeedDialFragment extends Fragment { speedDialLoaderListener.listen( getContext(), UiItemLoaderComponent.get(getContext()) - .speedDialUiItemLoader() + .speedDialUiItemMutator() .starContact(data.getData()), this::onSpeedDialUiItemListLoaded, throwable -> { @@ -173,7 +175,7 @@ public class SpeedDialFragment extends Fragment { // TODO(calderwoodra): Use DiffUtil to properly update and animate the change adapter.setSpeedDialUiItems( UiItemLoaderComponent.get(getContext()) - .speedDialUiItemLoader() + .speedDialUiItemMutator() .insertDuoChannels(getContext(), speedDialUiItems)); adapter.notifyDataSetChanged(); if (getActivity() != null) { @@ -187,6 +189,18 @@ public class SpeedDialFragment extends Fragment { super.onPause(); contextMenu.hideMenu(); contextMenuBackground.setVisibility(View.GONE); + Futures.addCallback( + DialerExecutorComponent.get(getContext()) + .backgroundExecutor() + .submit( + () -> { + UiItemLoaderComponent.get(getContext()) + .speedDialUiItemMutator() + .updatePinnedPosition(adapter.getSpeedDialUiItems()); + return null; + }), + new DefaultFutureCallback<>(), + DialerExecutorComponent.get(getContext()).backgroundExecutor()); } @Override diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java index 89aed8f37..181f9eca7 100644 --- a/java/com/android/dialer/speeddial/database/SpeedDialEntry.java +++ b/java/com/android/dialer/speeddial/database/SpeedDialEntry.java @@ -20,6 +20,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; +import com.google.common.base.Optional; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -35,6 +36,9 @@ public abstract class SpeedDialEntry { @Nullable public abstract Long id(); + /** Position the contact is pinned to in the UI. Will be absent if it hasn't be set yet. */ + public abstract Optional<Integer> pinnedPosition(); + /** @see {@link Contacts#_ID} */ public abstract long contactId(); @@ -53,7 +57,7 @@ public abstract class SpeedDialEntry { public abstract Builder toBuilder(); public static Builder builder() { - return new AutoValue_SpeedDialEntry.Builder(); + return new AutoValue_SpeedDialEntry.Builder().setPinnedPosition(Optional.absent()); } /** Builder class for speed dial entry. */ @@ -62,6 +66,8 @@ public abstract class SpeedDialEntry { public abstract Builder setId(Long id); + public abstract Builder setPinnedPosition(Optional<Integer> pinnedPosition); + public abstract Builder setContactId(long contactId); public abstract Builder setLookupKey(String lookupKey); diff --git a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java index 544bb3613..1416a203d 100644 --- a/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java +++ b/java/com/android/dialer/speeddial/database/SpeedDialEntryDatabaseHelper.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import com.android.dialer.common.Assert; import com.android.dialer.common.database.Selection; import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.util.ArrayList; @@ -38,12 +39,20 @@ import java.util.List; public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper implements SpeedDialEntryDao { + /** + * If the pinned position is absent, then we need to write an impossible value in the table like + * -1 so that it doesn't default to 0. When we read this value from the table, we'll translate it + * to Optional.absent() in the resulting {@link SpeedDialEntry}. + */ + private static final int PINNED_POSITION_ABSENT = -1; + private static final int DATABASE_VERSION = 2; private static final String DATABASE_NAME = "CPSpeedDialEntry"; // Column names private static final String TABLE_NAME = "speed_dial_entries"; private static final String ID = "id"; + private static final String PINNED_POSITION = "pinned_position"; private static final String CONTACT_ID = "contact_id"; private static final String LOOKUP_KEY = "lookup_key"; private static final String PHONE_NUMBER = "phone_number"; @@ -53,12 +62,13 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper // Column positions private static final int POSITION_ID = 0; - private static final int POSITION_CONTACT_ID = 1; - private static final int POSITION_LOOKUP_KEY = 2; - private static final int POSITION_PHONE_NUMBER = 3; - private static final int POSITION_PHONE_TYPE = 4; - private static final int POSITION_PHONE_LABEL = 5; - private static final int POSITION_PHONE_TECHNOLOGY = 6; + private static final int POSITION_PINNED_POSITION = 1; + private static final int POSITION_CONTACT_ID = 2; + private static final int POSITION_LOOKUP_KEY = 3; + private static final int POSITION_PHONE_NUMBER = 4; + private static final int POSITION_PHONE_TYPE = 5; + private static final int POSITION_PHONE_LABEL = 6; + private static final int POSITION_PHONE_TECHNOLOGY = 7; // Create Table Query private static final String CREATE_TABLE_SQL = @@ -66,6 +76,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper + TABLE_NAME + " (" + (ID + " integer primary key, ") + + (PINNED_POSITION + " integer, ") + (CONTACT_ID + " integer, ") + (LOOKUP_KEY + " text, ") + (PHONE_NUMBER + " text, ") @@ -119,11 +130,17 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper .build(); } + Optional<Integer> pinnedPosition = Optional.of(cursor.getInt(POSITION_PINNED_POSITION)); + if (pinnedPosition.or(PINNED_POSITION_ABSENT) == PINNED_POSITION_ABSENT) { + pinnedPosition = Optional.absent(); + } + SpeedDialEntry entry = SpeedDialEntry.builder() .setDefaultChannel(channel) .setContactId(cursor.getLong(POSITION_CONTACT_ID)) .setLookupKey(cursor.getString(POSITION_LOOKUP_KEY)) + .setPinnedPosition(pinnedPosition) .setId(cursor.getLong(POSITION_ID)) .build(); entries.add(entry); @@ -226,6 +243,7 @@ public final class SpeedDialEntryDatabaseHelper extends SQLiteOpenHelper if (includeId) { values.put(ID, entry.id()); } + values.put(PINNED_POSITION, entry.pinnedPosition().or(PINNED_POSITION_ABSENT)); values.put(CONTACT_ID, entry.contactId()); values.put(LOOKUP_KEY, entry.lookupKey()); if (entry.defaultChannel() != null) { diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java index 9bda3fb31..a2bdfb89a 100644 --- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java +++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItem.java @@ -25,6 +25,7 @@ import com.android.dialer.common.Assert; import com.android.dialer.speeddial.database.SpeedDialEntry; import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; import com.google.auto.value.AutoValue; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; @@ -83,7 +84,9 @@ public abstract class SpeedDialUiItem { } public static Builder builder() { - return new AutoValue_SpeedDialUiItem.Builder().setChannels(ImmutableList.of()); + return new AutoValue_SpeedDialUiItem.Builder() + .setChannels(ImmutableList.of()) + .setPinnedPosition(Optional.absent()); } /** @@ -139,6 +142,7 @@ public abstract class SpeedDialUiItem { public SpeedDialEntry buildSpeedDialEntry() { return SpeedDialEntry.builder() .setId(speedDialEntryId()) + .setPinnedPosition(pinnedPosition()) .setLookupKey(lookupKey()) .setContactId(contactId()) .setDefaultChannel(defaultChannel()) @@ -212,6 +216,9 @@ public abstract class SpeedDialUiItem { @Nullable public abstract Long speedDialEntryId(); + /** @see SpeedDialEntry#pinnedPosition() */ + public abstract Optional<Integer> pinnedPosition(); + /** @see android.provider.ContactsContract.Contacts#DISPLAY_NAME */ public abstract String name(); @@ -255,6 +262,8 @@ public abstract class SpeedDialUiItem { /** Set to null if {@link #isStarred()} is false. */ public abstract Builder setSpeedDialEntryId(@Nullable Long id); + public abstract Builder setPinnedPosition(Optional<Integer> pinnedPosition); + public abstract Builder setName(String name); public abstract Builder setContactId(long contactId); diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemMutator.java index 921468773..5dae2efab 100644 --- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java +++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemMutator.java @@ -17,11 +17,14 @@ package com.android.dialer.speeddial.loader; import android.annotation.TargetApi; +import android.content.ContentProviderOperation; import android.content.ContentValues; import android.content.Context; +import android.content.OperationApplicationException; import android.database.Cursor; import android.net.Uri; import android.os.Build.VERSION_CODES; +import android.os.RemoteException; import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; @@ -43,6 +46,7 @@ import com.android.dialer.speeddial.database.SpeedDialEntry; import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; import com.android.dialer.speeddial.database.SpeedDialEntryDao; import com.android.dialer.speeddial.database.SpeedDialEntryDatabaseHelper; +import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListenableFuture; @@ -76,7 +80,7 @@ import javax.inject.Singleton; @SuppressWarnings("AndroidApiChecker") @TargetApi(VERSION_CODES.N) @Singleton -public final class SpeedDialUiItemLoader { +public final class SpeedDialUiItemMutator { private static final int MAX_DUO_SUGGESTIONS = 3; @@ -87,7 +91,7 @@ public final class SpeedDialUiItemLoader { private final ContactsPreferences contactsPreferences; @Inject - public SpeedDialUiItemLoader( + public SpeedDialUiItemMutator( @ApplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService backgroundExecutor) { this.appContext = appContext; @@ -127,7 +131,7 @@ public final class SpeedDialUiItemLoader { null, null)) { if (cursor == null) { - LogUtil.e("SpeedDialUiItemLoader.insertNewContactEntry", "Cursor was null"); + LogUtil.e("SpeedDialUiItemMutator.insertNewContactEntry", "Cursor was null"); return loadSpeedDialUiItemsInternal(); } Assert.checkArgument(cursor.moveToFirst(), "Cursor should never be empty"); @@ -285,7 +289,7 @@ public final class SpeedDialUiItemLoader { null, null)) { if (cursor == null) { - LogUtil.e("SpeedDialUiItemLoader.updateContactIdsAndLookupKeys", "null cursor"); + LogUtil.e("SpeedDialUiItemMutator.updateContactIdsAndLookupKeys", "null cursor"); return new ArrayList<>(); } if (cursor.getCount() == 0) { @@ -339,9 +343,11 @@ public final class SpeedDialUiItemLoader { SpeedDialUiItem item = SpeedDialUiItem.fromCursor(cursor); for (SpeedDialEntry entry : entries) { if (entry.contactId() == item.contactId()) { - // Update the id to match it's corresponding SpeedDialEntry. + // Update the id and pinned position to match it's corresponding SpeedDialEntry. SpeedDialUiItem.Builder entrySpeedDialItem = - item.toBuilder().setSpeedDialEntryId(entry.id()); + item.toBuilder() + .setSpeedDialEntryId(entry.id()) + .setPinnedPosition(entry.pinnedPosition()); // Preserve the default channel if it didn't change/still exists Channel defaultChannel = entry.defaultChannel(); @@ -405,7 +411,7 @@ public final class SpeedDialUiItemLoader { .getContentResolver() .query(strequentUri, new String[] {Phone.CONTACT_ID}, null, null, null)) { if (cursor == null) { - LogUtil.e("SpeedDialUiItemLoader.getStrequentContacts", "null cursor"); + LogUtil.e("SpeedDialUiItemMutator.getStrequentContacts", "null cursor"); return new ArrayList<>(); } if (cursor.getCount() == 0) { @@ -430,7 +436,7 @@ public final class SpeedDialUiItemLoader { null)) { List<SpeedDialUiItem> contacts = new ArrayList<>(); if (cursor == null) { - LogUtil.e("SpeedDialUiItemLoader.getStrequentContacts", "null cursor"); + LogUtil.e("SpeedDialUiItemMutator.getStrequentContacts", "null cursor"); return new ArrayList<>(); } if (cursor.getCount() == 0) { @@ -444,6 +450,75 @@ public final class SpeedDialUiItemLoader { } /** + * Persists the position of the {@link SpeedDialUiItem items} as the pinned position according to + * the order they were passed in. + */ + @WorkerThread + public void updatePinnedPosition(List<SpeedDialUiItem> speedDialUiItems) { + Assert.isWorkerThread(); + if (speedDialUiItems == null || speedDialUiItems.isEmpty()) { + return; + } + + // Update the positions in the SpeedDialEntry database + ImmutableList.Builder<SpeedDialEntry> entriesToUpdate = ImmutableList.builder(); + for (int i = 0; i < speedDialUiItems.size(); i++) { + SpeedDialUiItem item = speedDialUiItems.get(i); + if (item.isStarred()) { + entriesToUpdate.add( + item.buildSpeedDialEntry().toBuilder().setPinnedPosition(Optional.of(i)).build()); + } + } + getSpeedDialEntryDao().update(entriesToUpdate.build()); + + // Update the positions in CP2 + // Build a list of SpeedDialUiItems where each contact is only represented once but the order + // is maintained. For example, assume you have a list of contacts with contact ids: + // > { 1, 1, 2, 1, 2, 3 } + // This list will be reduced to: + // > { 1, 2, 3 } + // and their positions in the resulting list will be written to the CP2 Contacts.PINNED column. + List<SpeedDialUiItem> cp2SpeedDialUiItems = new ArrayList<>(); + Set<Long> contactIds = new ArraySet<>(); + for (SpeedDialUiItem item : speedDialUiItems) { + if (contactIds.add(item.contactId())) { + cp2SpeedDialUiItems.add(item); + } + } + + // Code copied from PhoneFavoritesTileAdapter#handleDrop + ArrayList<ContentProviderOperation> operations = new ArrayList<>(); + for (int i = 0; i < cp2SpeedDialUiItems.size(); i++) { + SpeedDialUiItem item = cp2SpeedDialUiItems.get(i); + // Pinned positions in the database start from 1 instead of being zero-indexed like + // arrays, so offset by 1. + int databasePinnedPosition = i + 1; + if (item.pinnedPosition().isPresent() + && item.pinnedPosition().get() == databasePinnedPosition) { + continue; + } + + Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(item.contactId())); + ContentValues values = new ContentValues(); + values.put(Contacts.PINNED, databasePinnedPosition); + operations.add(ContentProviderOperation.newUpdate(uri).withValues(values).build()); + } + if (operations.isEmpty()) { + // Nothing to update + return; + } + try { + appContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operations); + // TODO(calderwoodra): log + } catch (RemoteException | OperationApplicationException e) { + LogUtil.e( + "SpeedDialUiItemMutator.updatePinnedPosition", + "Exception thrown when pinning contacts", + e); + } + } + + /** * Returns a new list with duo reachable channels inserted. Duo channels won't replace ViLTE * channels. */ diff --git a/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java b/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java index 7d01b4380..852908409 100644 --- a/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java +++ b/java/com/android/dialer/speeddial/loader/UiItemLoaderComponent.java @@ -24,7 +24,7 @@ import dagger.Subcomponent; @Subcomponent public abstract class UiItemLoaderComponent { - public abstract SpeedDialUiItemLoader speedDialUiItemLoader(); + public abstract SpeedDialUiItemMutator speedDialUiItemMutator(); public static UiItemLoaderComponent get(Context context) { return ((UiItemLoaderComponent.HasComponent) diff --git a/java/com/android/incallui/AndroidManifest.xml b/java/com/android/incallui/AndroidManifest.xml index 832a5e874..7286b0db7 100644 --- a/java/com/android/incallui/AndroidManifest.xml +++ b/java/com/android/incallui/AndroidManifest.xml @@ -106,6 +106,13 @@ android:theme="@style/Theme.Incall.DialogHolder" /> + <activity + android:excludeFromRecents="true" + android:exported="false" + android:name="com.android.incallui.PostCharDialogActivity" + android:noHistory="true" + android:theme="@style/Theme.Incall.DialogHolder"/> + <!-- BroadcastReceiver for receiving Intents from Notification mechanism. --> <receiver android:directBootAware="true" diff --git a/java/com/android/incallui/InCallActivity.java b/java/com/android/incallui/InCallActivity.java index 2b6c2b49d..02335b6e4 100644 --- a/java/com/android/incallui/InCallActivity.java +++ b/java/com/android/incallui/InCallActivity.java @@ -145,8 +145,6 @@ public class InCallActivity extends TransactionSafeFragmentActivity private View pseudoBlackScreenOverlay; private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment; private String dtmfTextToPrepopulate; - private String showPostCharWaitDialogCallId; - private String showPostCharWaitDialogChars; private boolean allowOrientationChange; private boolean animateDialpadOnShow; private boolean didShowAnswerScreen; @@ -160,7 +158,6 @@ public class InCallActivity extends TransactionSafeFragmentActivity private boolean isRecreating; // whether the activity is going to be recreated private boolean isVisible; private boolean needDismissPendingDialogs; - private boolean showPostCharWaitDialogOnResume; private boolean touchDownWhenPseudoScreenOff; private int[] backgroundDrawableColors; @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE; @@ -531,10 +528,6 @@ public class InCallActivity extends TransactionSafeFragmentActivity } updateNavigationBar(isDialpadVisible()); - if (showPostCharWaitDialogOnResume) { - showDialogForPostCharWait(showPostCharWaitDialogCallId, showPostCharWaitDialogChars); - } - CallList.getInstance() .onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false)); @@ -1016,18 +1009,8 @@ public class InCallActivity extends TransactionSafeFragmentActivity } public void showDialogForPostCharWait(String callId, String chars) { - if (isVisible) { - PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); - fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT); - - showPostCharWaitDialogOnResume = false; - showPostCharWaitDialogCallId = null; - showPostCharWaitDialogChars = null; - } else { - showPostCharWaitDialogOnResume = true; - showPostCharWaitDialogCallId = callId; - showPostCharWaitDialogChars = chars; - } + PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); + fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT); } public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) { diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java index 526cc64d3..6e7daf551 100644 --- a/java/com/android/incallui/InCallPresenter.java +++ b/java/com/android/incallui/InCallPresenter.java @@ -1271,8 +1271,22 @@ public class InCallPresenter implements CallList.Listener, AudioModeProvider.Aud } public void onPostDialCharWait(String callId, String chars) { - if (isActivityStarted()) { + // If not visible, inCallActivity is stopped. Starting from P, calling recreate() will destroy + // the old activity instance and create a new instance immediately. Previously, the old activity + // went through its lifecycle from create to destroy before creating a new instance. + // So this case doesn't work now: make a call with char WAIT, leave in call UI, call gets + // connected, and go back to in call UI to see the dialog. + // So we should show dialog in an empty activity if inCallActivity is not visible. And it also + // helps with background calling. + if (isActivityStarted() && inCallActivity.isVisible()) { inCallActivity.showDialogForPostCharWait(callId, chars); + } else { + Intent intent = new Intent(context, PostCharDialogActivity.class); + // Prevent showing MainActivity with PostCharDialogActivity on above + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.putExtra(PostCharDialogActivity.EXTRA_CALL_ID, callId); + intent.putExtra(PostCharDialogActivity.EXTRA_POST_DIAL_STRING, chars); + context.startActivity(intent); } } diff --git a/java/com/android/incallui/PostCharDialogActivity.java b/java/com/android/incallui/PostCharDialogActivity.java new file mode 100644 index 000000000..dcdc9d663 --- /dev/null +++ b/java/com/android/incallui/PostCharDialogActivity.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.incallui; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import com.android.incallui.call.CallList; +import com.android.incallui.call.DialerCall; + +/** + * Activity that contains an alert dialog with OK and Cancel buttons to allow user to Accept or + * Reject the WAIT inserted as part of the Dial string. + */ +public class PostCharDialogActivity extends AppCompatActivity implements CallList.Listener { + + public static final String EXTRA_CALL_ID = "extra_call_id"; + public static final String EXTRA_POST_DIAL_STRING = "extra_post_dial_string"; + private static final String TAG_INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi"; + + private String callId; + + @Override + protected void onCreate(@Nullable Bundle bundle) { + super.onCreate(bundle); + + callId = getIntent().getStringExtra(EXTRA_CALL_ID); + String postDialString = getIntent().getStringExtra(EXTRA_POST_DIAL_STRING); + if (callId == null || postDialString == null) { + finish(); + return; + } + + PostCharDialogFragment fragment = new PostCharDialogFragment(callId, postDialString); + fragment.show(getSupportFragmentManager(), TAG_INTERNATIONAL_CALL_ON_WIFI); + + CallList.getInstance().addListener(this); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + CallList.getInstance().removeListener(this); + } + + @Override + protected void onPause() { + super.onPause(); + // We don't expect the activity to resume, except for orientation change. + if (!isChangingConfigurations()) { + finish(); + } + } + + @Override + public void onDisconnect(DialerCall call) { + if (callId.equals(call.getId())) { + finish(); + } + } + + @Override + public void onIncomingCall(DialerCall call) {} + + @Override + public void onUpgradeToVideo(DialerCall call) {} + + @Override + public void onUpgradeToRtt(DialerCall call, int rttRequestId) {} + + @Override + public void onSessionModificationStateChange(DialerCall call) {} + + @Override + public void onCallListChange(CallList callList) {} + + @Override + public void onWiFiToLteHandover(DialerCall call) {} + + @Override + public void onHandoverToWifiFailed(DialerCall call) {} + + @Override + public void onInternationalCallOnWifi(@NonNull DialerCall call) {} +} diff --git a/java/com/android/incallui/PostCharDialogFragment.java b/java/com/android/incallui/PostCharDialogFragment.java index 4bcc68e33..1d06fd487 100644 --- a/java/com/android/incallui/PostCharDialogFragment.java +++ b/java/com/android/incallui/PostCharDialogFragment.java @@ -55,7 +55,8 @@ public class PostCharDialogFragment extends DialogFragment { buf.append(getResources().getText(R.string.wait_prompt_str)); buf.append(postDialStr); - final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + final AlertDialog.Builder builder = + new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme); builder.setMessage(buf.toString()); builder.setPositiveButton( diff --git a/java/com/android/incallui/call/DialerCall.java b/java/com/android/incallui/call/DialerCall.java index 9dfe7abcb..da05b9d2a 100644 --- a/java/com/android/incallui/call/DialerCall.java +++ b/java/com/android/incallui/call/DialerCall.java @@ -192,6 +192,7 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa private volatile boolean feedbackRequested = false; @Nullable private PreferredAccountRecorder preferredAccountRecorder; + private boolean isCallRemoved; public static String getNumberFromHandle(Uri handle) { return handle == null ? "" : handle.getSchemeSpecificPart(); @@ -1608,15 +1609,17 @@ public class DialerCall implements VideoTechListener, StateChangedListener, Capa void onRemovedFromCallList() { LogUtil.enterBlock("DialerCall.onRemovedFromCallList"); // Ensure we clean up when this call is removed. - videoTechManager.dispatchRemovedFromCallList(); - if (rttTranscript != null) { + if (videoTechManager != null) { + videoTechManager.dispatchRemovedFromCallList(); + } + // TODO(a bug): Add tests for it to make sure no crash on subsequent call to this method. + if (rttTranscript != null && !isCallRemoved) { Futures.addCallback( RttTranscriptUtil.saveRttTranscript(context, rttTranscript), new DefaultFutureCallback<>(), MoreExecutors.directExecutor()); - // Sets to null so it won't be saved again when called multiple times. - rttTranscript = null; } + isCallRemoved = true; } public com.android.dialer.logging.VideoTech.Type getSelectedAvailableVideoTechType() { diff --git a/java/com/android/incallui/rtt/impl/RttChatFragment.java b/java/com/android/incallui/rtt/impl/RttChatFragment.java index 7bfa100ea..47036cd43 100644 --- a/java/com/android/incallui/rtt/impl/RttChatFragment.java +++ b/java/com/android/incallui/rtt/impl/RttChatFragment.java @@ -461,6 +461,12 @@ public class RttChatFragment extends Fragment + SystemClock.elapsedRealtime()); chronometer.start(); isTimerStarted = true; + editText.setVisibility(View.VISIBLE); + submitButton.setVisibility(View.VISIBLE); + editText.setFocusableInTouchMode(true); + if (editText.requestFocus()) { + UiUtil.openKeyboardFrom(getContext(), editText); + } } if (primaryCallState.state() == State.DIALING) { showWaitingForJoinBanner(); diff --git a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml index ea7ff1095..f995185b1 100644 --- a/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml +++ b/java/com/android/incallui/rtt/impl/res/layout/frag_rtt_chat.xml @@ -54,7 +54,8 @@ android:minHeight="53dp" android:textColor="#DD000000" android:textColorHint="#757575" - android:textSize="16sp"/> + android:textSize="16sp" + android:visibility="gone"/> <ImageButton android:id="@+id/rtt_chat_submit_button" android:layout_width="55dp" @@ -65,7 +66,8 @@ android:backgroundTintMode="multiply" android:contentDescription="@string/content_description_rtt_check_button" android:src="@drawable/quantum_ic_done_vd_theme_24" - android:tint="@color/submit_button_color"/> + android:tint="@color/submit_button_color" + android:visibility="gone"/> </LinearLayout> <FrameLayout |