diff options
Diffstat (limited to 'java/com')
8 files changed, 447 insertions, 9 deletions
diff --git a/java/com/android/dialer/speeddial/DisambigDialog.java b/java/com/android/dialer/speeddial/DisambigDialog.java new file mode 100644 index 000000000..ca02f41eb --- /dev/null +++ b/java/com/android/dialer/speeddial/DisambigDialog.java @@ -0,0 +1,244 @@ +/* + * 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.speeddial; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.content.ContentResolver; +import android.content.res.Resources; +import android.database.Cursor; +import android.os.Bundle; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.support.annotation.Nullable; +import android.support.annotation.VisibleForTesting; +import android.text.TextUtils; +import android.util.ArraySet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.android.dialer.callintent.CallInitiationType; +import com.android.dialer.callintent.CallIntentBuilder; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutor.Worker; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.duo.DuoComponent; +import com.android.dialer.precall.PreCall; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; + +/** Disambiguation dialog for favorite contacts in {@link SpeedDialFragment}. */ +public class DisambigDialog extends DialogFragment { + + @VisibleForTesting public static final String DISAMBIG_DIALOG_TAG = "disambig_dialog"; + private static final String DISAMBIG_DIALOG_WORKER_TAG = "disambig_dialog_worker"; + + private final Set<String> phoneNumbers = new ArraySet<>(); + private LinearLayout container; + private String lookupKey; + + /** Show a disambiguation dialog for a starred contact without a favorite communication avenue. */ + public static DisambigDialog show(String lookupKey, FragmentManager manager) { + DisambigDialog dialog = new DisambigDialog(); + dialog.lookupKey = lookupKey; + dialog.show(manager, DISAMBIG_DIALOG_TAG); + return dialog; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + LayoutInflater inflater = getActivity().getLayoutInflater(); + View view = inflater.inflate(R.layout.disambig_dialog_layout, null, false); + container = view.findViewById(R.id.communication_avenue_container); + return new AlertDialog.Builder(getActivity()).setView(view).create(); + } + + @Override + public void onResume() { + super.onResume(); + lookupContactInfo(); + } + + @Override + public void onPause() { + super.onPause(); + // TODO(calderwoodra): for simplicity, just dismiss the dialog on configuration change and + // consider changing this later. + dismiss(); + } + + private void lookupContactInfo() { + DialerExecutorComponent.get(getContext()) + .dialerExecutorFactory() + .createUiTaskBuilder( + getFragmentManager(), + DISAMBIG_DIALOG_WORKER_TAG, + new LookupContactInfoWorker(getContext().getContentResolver())) + .onSuccess(this::insertOptions) + .onFailure(this::onLookupFailed) + .build() + .executeParallel(lookupKey); + } + + /** + * Inflates and inserts the following in the dialog: + * + * <ul> + * <li>Header for each unique phone number + * <li>Clickable video option if the phone number is video reachable (ViLTE, Duo) + * <li>Clickable voice option + * </ul> + */ + private void insertOptions(Cursor cursor) { + if (!cursorIsValid(cursor)) { + dismiss(); + return; + } + + do { + String number = cursor.getString(LookupContactInfoWorker.NUMBER_INDEX); + // TODO(calderwoodra): improve this to include fuzzy matching + if (phoneNumbers.add(number)) { + insertOption( + number, + getLabel(getContext().getResources(), cursor), + isVideoReachable(cursor, number)); + } + } while (cursor.moveToNext()); + cursor.close(); + // TODO(calderwoodra): set max height of the scrollview. Might need to override onMeasure. + } + + /** Returns true if the given number is ViLTE reachable or Duo reachable. */ + private boolean isVideoReachable(Cursor cursor, String number) { + boolean isVideoReachable = cursor.getInt(LookupContactInfoWorker.PHONE_PRESENCE_INDEX) == 1; + if (!isVideoReachable) { + isVideoReachable = DuoComponent.get(getContext()).getDuo().isReachable(getContext(), number); + } + return isVideoReachable; + } + + /** Inserts a group of options for a specific phone number. */ + private void insertOption(String number, String phoneType, boolean isVideoReachable) { + View view = + getActivity() + .getLayoutInflater() + .inflate(R.layout.disambig_option_layout, container, false); + ((TextView) view.findViewById(R.id.phone_type)).setText(phoneType); + ((TextView) view.findViewById(R.id.phone_number)).setText(number); + + if (isVideoReachable) { + View videoOption = view.findViewById(R.id.video_call_container); + videoOption.setOnClickListener(v -> onVideoOptionClicked(number)); + videoOption.setVisibility(View.VISIBLE); + } + View voiceOption = view.findViewById(R.id.voice_call_container); + voiceOption.setOnClickListener(v -> onVoiceOptionClicked(number)); + container.addView(view); + } + + private void onVideoOptionClicked(String number) { + // TODO(calderwoodra): save this option if remember is checked + // TODO(calderwoodra): place a duo call if possible + PreCall.start( + getContext(), + new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL).setIsVideoCall(true)); + } + + private void onVoiceOptionClicked(String number) { + // TODO(calderwoodra): save this option if remember is checked + PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL)); + } + + // TODO(calderwoodra): handle CNAP and cequint types. + // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType + private static String getLabel(Resources resources, Cursor cursor) { + int numberType = cursor.getInt(LookupContactInfoWorker.PHONE_TYPE_INDEX); + String numberLabel = cursor.getString(LookupContactInfoWorker.PHONE_LABEL_INDEX); + + // Returns empty label instead of "custom" if the custom label is empty. + if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) { + return ""; + } + return (String) Phone.getTypeLabel(resources, numberType, numberLabel); + } + + // Checks if the cursor is valid and logs an error if there are any issues. + private static boolean cursorIsValid(Cursor cursor) { + if (cursor == null) { + LogUtil.e("DisambigDialog.insertOptions", "cursor null."); + return false; + } else if (cursor.isClosed()) { + LogUtil.e("DisambigDialog.insertOptions", "cursor closed."); + cursor.close(); + return false; + } else if (!cursor.moveToFirst()) { + LogUtil.e("DisambigDialog.insertOptions", "cursor empty."); + cursor.close(); + return false; + } + return true; + } + + private void onLookupFailed(Throwable throwable) { + LogUtil.e("DisambigDialog.onLookupFailed", null, throwable); + insertOptions(null); + } + + private static class LookupContactInfoWorker implements Worker<String, Cursor> { + + static final int NUMBER_INDEX = 0; + static final int PHONE_TYPE_INDEX = 1; + static final int PHONE_LABEL_INDEX = 2; + static final int PHONE_PRESENCE_INDEX = 3; + + private static final String[] projection = + new String[] {Phone.NUMBER, Phone.TYPE, Phone.LABEL, Phone.CARRIER_PRESENCE}; + private final ContentResolver resolver; + + LookupContactInfoWorker(ContentResolver resolver) { + this.resolver = resolver; + } + + @Nullable + @Override + public Cursor doInBackground(@Nullable String lookupKey) throws Throwable { + if (TextUtils.isEmpty(lookupKey)) { + LogUtil.e("LookupConctactInfoWorker.doInBackground", "contact id unsest."); + return null; + } + return resolver.query( + Phone.CONTENT_URI, projection, Phone.LOOKUP_KEY + " = ?", new String[] {lookupKey}, null); + } + } + + @VisibleForTesting + public static String[] getProjectionForTesting() { + ArrayList<String> projection = + new ArrayList<>(Arrays.asList(LookupContactInfoWorker.projection)); + projection.add(Phone.LOOKUP_KEY); + return projection.toArray(new String[projection.size()]); + } + + @VisibleForTesting + public LinearLayout getContainer() { + return container; + } +} diff --git a/java/com/android/dialer/speeddial/FavoritesViewHolder.java b/java/com/android/dialer/speeddial/FavoritesViewHolder.java index 0cde71693..c25b05ead 100644 --- a/java/com/android/dialer/speeddial/FavoritesViewHolder.java +++ b/java/com/android/dialer/speeddial/FavoritesViewHolder.java @@ -45,8 +45,10 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder private final TextView phoneType; private final FrameLayout videoCallIcon; + private boolean hasDefaultNumber; private boolean isVideoCall; private String number; + private String lookupKey; public FavoritesViewHolder(View view, FavoriteContactsListener listener) { super(view); @@ -67,7 +69,7 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME); long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID); - String lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY); + lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY); Uri contactUri = Contacts.getLookupUri(contactId, lookupKey); String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI); @@ -82,6 +84,9 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder nameView.setText(name); phoneType.setText(getLabel(context.getResources(), cursor)); videoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE); + + // TODO(calderwoodra): Update this to include communication avenues also + hasDefaultNumber = cursor.getInt(StrequentContactsCursorLoader.PHONE_IS_SUPER_PRIMARY) != 0; } // TODO(calderwoodra): handle CNAP and cequint types. @@ -99,7 +104,11 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder @Override public void onClick(View v) { - listener.onClick(number, isVideoCall); + if (hasDefaultNumber) { + listener.onClick(number, isVideoCall); + } else { + listener.onAmbiguousContactClicked(lookupKey); + } } @Override @@ -112,6 +121,9 @@ public class FavoritesViewHolder extends RecyclerView.ViewHolder /** Listener/callback for {@link FavoritesViewHolder} actions. */ public interface FavoriteContactsListener { + /** Called when the user clicks on a favorite contact that doesn't have a default number. */ + void onAmbiguousContactClicked(String contactId); + /** Called when the user clicks on a favorite contact. */ void onClick(String number, boolean isVideoCall); diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java index 08861dae9..979c894fe 100644 --- a/java/com/android/dialer/speeddial/SpeedDialFragment.java +++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java @@ -98,6 +98,11 @@ public class SpeedDialFragment extends Fragment { private class SpeedDialFavoritesListener implements FavoriteContactsListener { @Override + public void onAmbiguousContactClicked(String lookupKey) { + DisambigDialog.show(lookupKey, getFragmentManager()); + } + + @Override public void onClick(String number, boolean isVideoCall) { // TODO(calderwoodra): add logic for duo video calls PreCall.start( diff --git a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java index f5f0045e0..e9e3e32da 100644 --- a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java +++ b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java @@ -18,7 +18,6 @@ package com.android.dialer.speeddial; import android.content.Context; import android.content.CursorLoader; -import android.database.ContentObserver; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; @@ -58,8 +57,6 @@ final class StrequentContactsCursorLoader extends CursorLoader { Phone.CONTACT_ID, // 11 }; - private final ContentObserver contentObserver = new ForceLoadContentObserver(); - StrequentContactsCursorLoader(Context context) { super( context, @@ -97,8 +94,4 @@ final class StrequentContactsCursorLoader extends CursorLoader { public Cursor loadInBackground() { return SpeedDialCursor.newInstance(super.loadInBackground()); } - - ContentObserver getContentObserver() { - return contentObserver; - } } diff --git a/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml b/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml new file mode 100644 index 000000000..356205805 --- /dev/null +++ b/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml @@ -0,0 +1,66 @@ +<?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 + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/disambig_dialog_title" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:minHeight="64dp" + android:paddingStart="24dp" + android:paddingEnd="24dp" + android:elevation="1dp" + android:text="@string/speed_dial_disambig_dialog_title" + android:textSize="20sp" + android:textColor="@color/dialer_primary_text_color" + android:fontFamily="sans-serif-medium" + android:background="@android:color/white"/> + + <ScrollView + android:id="@+id/disambig_scrollview" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <LinearLayout + android:id="@+id/communication_avenue_container" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + </ScrollView> + + <FrameLayout + android:id="@+id/remember_this_choice_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom" + android:minHeight="56dp" + android:padding="12dp" + android:elevation="4dp" + android:background="@android:color/white"> + + <CheckBox + android:id="@+id/remember_this_choice_checkbox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/speed_dial_remember_this_choice"/> + </FrameLayout> +</LinearLayout>
\ No newline at end of file diff --git a/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml b/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml new file mode 100644 index 000000000..097ac4084 --- /dev/null +++ b/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml @@ -0,0 +1,103 @@ +<?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 + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/disambig_option_container" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="56dp" + android:layout_marginBottom="8dp"> + + <LinearLayout + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="56dp" + android:gravity="center_vertical" + android:paddingStart="24dp" + android:paddingEnd="24dp"> + + <TextView + android:id="@+id/phone_type" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/PrimaryText"/> + + <TextView + android:id="@+id/phone_number" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + style="@style/SecondaryText"/> + </LinearLayout> + + <LinearLayout + android:id="@+id/video_call_container" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="24dp" + android:minHeight="56dp" + android:background="?android:attr/selectableItemBackground" + android:visibility="gone" + android:contentDescription="@string/disambig_option_video_call"> + + <ImageView + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center_vertical" + android:tint="@color/dialer_secondary_text_color" + android:src="@drawable/quantum_ic_videocam_vd_theme_24"/> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_gravity="center_vertical" + android:text="@string/disambig_option_video_call" + style="@style/PrimaryText"/> + </LinearLayout> + + <LinearLayout + android:id="@+id/voice_call_container" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingStart="24dp" + android:paddingEnd="24dp" + android:minHeight="56dp" + android:background="?android:attr/selectableItemBackground" + android:contentDescription="@string/disambig_option_voice_call"> + + <ImageView + android:id="@+id/disambig_option_icon" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_gravity="center_vertical" + android:tint="@color/dialer_secondary_text_color" + android:src="@drawable/quantum_ic_phone_vd_theme_24"/> + + <TextView + android:id="@+id/disambig_option_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="12dp" + android:layout_gravity="center_vertical" + android:text="@string/disambig_option_voice_call" + style="@style/PrimaryText"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/java/com/android/dialer/speeddial/res/values/dimens.xml b/java/com/android/dialer/speeddial/res/values/dimens.xml index 5929df8dd..74b509b73 100644 --- a/java/com/android/dialer/speeddial/res/values/dimens.xml +++ b/java/com/android/dialer/speeddial/res/values/dimens.xml @@ -15,4 +15,5 @@ ~ limitations under the License --> <resources> + <dimen name="scrollview_max_height">280dp</dimen> </resources>
\ No newline at end of file diff --git a/java/com/android/dialer/speeddial/res/values/strings.xml b/java/com/android/dialer/speeddial/res/values/strings.xml index d64d03575..677f772c5 100644 --- a/java/com/android/dialer/speeddial/res/values/strings.xml +++ b/java/com/android/dialer/speeddial/res/values/strings.xml @@ -24,6 +24,20 @@ <!-- text for a button that prompts the user to add a contact to their favorites. [CHAR LIMIT=12] --> <string name="speed_dial_add_button_text">Add</string> + <!-- text for a checkbox in a dialog that prompts the user to select a phone number from a list. + If the user checks this box, we will remember their selection and never ask for it again. [CHAR LIMIT=NONE]--> + <string name="speed_dial_remember_this_choice">Remember this choice</string> + + <!-- Title of a dialog asking the user to choose their favorite mode of communication for a + specific contact where communication modes are video calling and voice calling. [CHAR LIMIT=NONE]--> + <string name="speed_dial_disambig_dialog_title">Choose a Favorite mode</string> + + <!-- Text for a button that places a video call [CHAR LIMIT=15]--> + <string name="disambig_option_video_call">Video call</string> + + <!-- Text for a button that places a phone/voice call [CHAR LIMIT=15]--> + <string name="disambig_option_voice_call">Call</string> + <!-- Title for screen prompting the user to select a contact to mark as a favorite. [CHAR LIMIT=NONE] --> <string name="add_favorite_activity_title">Add Favorite</string> </resources>
\ No newline at end of file |