1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.dialer.speeddial; 18 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.support.annotation.VisibleForTesting; 23 import android.support.annotation.WorkerThread; 24 import android.support.v4.app.DialogFragment; 25 import android.support.v4.app.FragmentManager; 26 import android.support.v7.app.AlertDialog; 27 import android.text.TextUtils; 28 import android.util.ArraySet; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.widget.CheckBox; 32 import android.widget.ImageView; 33 import android.widget.LinearLayout; 34 import android.widget.LinearLayout.LayoutParams; 35 import android.widget.TextView; 36 import com.android.dialer.callintent.CallInitiationType; 37 import com.android.dialer.callintent.CallIntentBuilder; 38 import com.android.dialer.common.Assert; 39 import com.android.dialer.common.LogUtil; 40 import com.android.dialer.common.concurrent.DefaultFutureCallback; 41 import com.android.dialer.common.concurrent.DialerExecutorComponent; 42 import com.android.dialer.logging.DialerImpression; 43 import com.android.dialer.logging.Logger; 44 import com.android.dialer.precall.PreCall; 45 import com.android.dialer.speeddial.database.SpeedDialEntry; 46 import com.android.dialer.speeddial.database.SpeedDialEntry.Channel; 47 import com.android.dialer.speeddial.database.SpeedDialEntryDatabaseHelper; 48 import com.android.dialer.speeddial.loader.SpeedDialUiItem; 49 import com.google.common.collect.ImmutableList; 50 import com.google.common.util.concurrent.Futures; 51 import com.google.common.util.concurrent.ListenableFuture; 52 import java.util.List; 53 import java.util.Set; 54 55 /** Disambiguation dialog for favorite contacts in {@link SpeedDialFragment}. */ 56 public class DisambigDialog extends DialogFragment { 57 58 @VisibleForTesting public static final String FRAGMENT_TAG = "disambig_dialog"; 59 private final Set<String> phoneNumbers = new ArraySet<>(); 60 61 private SpeedDialUiItem speedDialUiItem; 62 @VisibleForTesting public List<Channel> channels; 63 @VisibleForTesting public LinearLayout container; 64 @VisibleForTesting public CheckBox rememberThisChoice; 65 66 /** Show a disambiguation dialog for a starred contact without a favorite communication avenue. */ show(SpeedDialUiItem speedDialUiItem, FragmentManager manager)67 public static DisambigDialog show(SpeedDialUiItem speedDialUiItem, FragmentManager manager) { 68 DisambigDialog dialog = new DisambigDialog(); 69 dialog.speedDialUiItem = speedDialUiItem; 70 dialog.channels = speedDialUiItem.channels(); 71 dialog.show(manager, FRAGMENT_TAG); 72 return dialog; 73 } 74 75 @Override onCreateDialog(Bundle savedInstanceState)76 public Dialog onCreateDialog(Bundle savedInstanceState) { 77 LayoutInflater inflater = getActivity().getLayoutInflater(); 78 // TODO(calderwoodra): set max height of the scrollview. Might need to override onMeasure. 79 View view = inflater.inflate(R.layout.disambig_dialog_layout, null, false); 80 container = view.findViewById(R.id.communication_avenue_container); 81 rememberThisChoice = view.findViewById(R.id.remember_this_choice_checkbox); 82 insertOptions(container.findViewById(R.id.communication_avenue_container), channels); 83 return new AlertDialog.Builder(getActivity()).setView(view).create(); 84 } 85 86 @Override onResume()87 public void onResume() { 88 super.onResume(); 89 getDialog() 90 .getWindow() 91 .setLayout( 92 getContext().getResources().getDimensionPixelSize(R.dimen.disambig_dialog_width), 93 LayoutParams.WRAP_CONTENT); 94 } 95 96 @Override onPause()97 public void onPause() { 98 super.onPause(); 99 // TODO(calderwoodra): for simplicity, just dismiss the dialog on configuration change and 100 // consider changing this later. 101 dismiss(); 102 } 103 104 /** 105 * Inflates and inserts the following in the dialog: 106 * 107 * <ul> 108 * <li>Header for each unique phone number 109 * <li>Clickable video option if the phone number is video reachable (ViLTE, Duo) 110 * <li>Clickable voice option 111 * </ul> 112 */ insertOptions(LinearLayout container, List<Channel> channels)113 private void insertOptions(LinearLayout container, List<Channel> channels) { 114 for (Channel channel : channels) { 115 // TODO(calderwoodra): use fuzzy number matcher 116 if (phoneNumbers.add(channel.number())) { 117 if (phoneNumbers.size() != 1) { 118 insertDivider(container); 119 } 120 insertHeader(container, channel.number(), channel.label()); 121 } 122 insertOption(container, channel); 123 } 124 } 125 insertDivider(LinearLayout container)126 private void insertDivider(LinearLayout container) { 127 View view = 128 getActivity() 129 .getLayoutInflater() 130 .inflate(R.layout.disambig_dialog_divider, container, false); 131 container.addView(view); 132 } 133 insertHeader(LinearLayout container, String number, String label)134 private void insertHeader(LinearLayout container, String number, String label) { 135 View view = 136 getActivity() 137 .getLayoutInflater() 138 .inflate(R.layout.disambig_option_header_layout, container, false); 139 String secondaryInfo = 140 TextUtils.isEmpty(label) 141 ? number 142 : getContext().getString(R.string.call_subject_type_and_number, label, number); 143 ((TextView) view.findViewById(R.id.disambig_header_phone_label)).setText(secondaryInfo); 144 container.addView(view); 145 } 146 147 /** Inserts a group of options for a specific phone number. */ insertOption(LinearLayout container, Channel channel)148 private void insertOption(LinearLayout container, Channel channel) { 149 View view = 150 getActivity() 151 .getLayoutInflater() 152 .inflate(R.layout.disambig_option_layout, container, false); 153 if (channel.isVideoTechnology()) { 154 View videoOption = view.findViewById(R.id.option_container); 155 videoOption.setOnClickListener(v -> onVideoOptionClicked(channel)); 156 videoOption.setContentDescription( 157 getActivity().getString(R.string.disambig_option_video_call)); 158 ((ImageView) view.findViewById(R.id.disambig_option_image)) 159 .setImageResource(R.drawable.quantum_ic_videocam_vd_theme_24); 160 ((TextView) view.findViewById(R.id.disambig_option_text)) 161 .setText(R.string.disambig_option_video_call); 162 } else { 163 View voiceOption = view.findViewById(R.id.option_container); 164 voiceOption.setOnClickListener(v -> onVoiceOptionClicked(channel)); 165 voiceOption.setContentDescription( 166 getActivity().getString(R.string.disambig_option_voice_call)); 167 ((ImageView) view.findViewById(R.id.disambig_option_image)) 168 .setImageResource(R.drawable.quantum_ic_phone_vd_theme_24); 169 ((TextView) view.findViewById(R.id.disambig_option_text)) 170 .setText(R.string.disambig_option_voice_call); 171 } 172 container.addView(view); 173 } 174 onVideoOptionClicked(Channel channel)175 private void onVideoOptionClicked(Channel channel) { 176 if (rememberThisChoice.isChecked()) { 177 Logger.get(getContext()).logImpression(DialerImpression.Type.FAVORITE_SET_VIDEO_DEFAULT); 178 setDefaultChannel(getContext().getApplicationContext(), speedDialUiItem, channel); 179 } 180 181 if (channel.technology() == Channel.DUO) { 182 Logger.get(getContext()) 183 .logImpression( 184 DialerImpression.Type.LIGHTBRINGER_VIDEO_REQUESTED_FOR_FAVORITE_CONTACT_DISAMBIG); 185 } 186 187 PreCall.start( 188 getContext(), 189 new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL_DISAMBIG_DIALOG) 190 .setAllowAssistedDial(true) 191 .setIsVideoCall(true) 192 .setIsDuoCall(channel.technology() == Channel.DUO)); 193 dismiss(); 194 } 195 onVoiceOptionClicked(Channel channel)196 private void onVoiceOptionClicked(Channel channel) { 197 if (rememberThisChoice.isChecked()) { 198 Logger.get(getContext()).logImpression(DialerImpression.Type.FAVORITE_SET_VOICE_DEFAULT); 199 setDefaultChannel(getContext().getApplicationContext(), speedDialUiItem, channel); 200 } 201 202 PreCall.start( 203 getContext(), 204 new CallIntentBuilder(channel.number(), CallInitiationType.Type.SPEED_DIAL_DISAMBIG_DIALOG) 205 .setAllowAssistedDial(true)); 206 dismiss(); 207 } 208 setDefaultChannel(Context appContext, SpeedDialUiItem item, Channel channel)209 private static void setDefaultChannel(Context appContext, SpeedDialUiItem item, Channel channel) { 210 LogUtil.enterBlock("DisambigDialog.setDefaultChannel"); 211 ListenableFuture<Void> future = 212 DialerExecutorComponent.get(appContext) 213 .backgroundExecutor() 214 .submit( 215 () -> { 216 updateDatabaseEntry(appContext, item, channel); 217 return null; 218 }); 219 Futures.addCallback( 220 future, 221 new DefaultFutureCallback<>(), 222 DialerExecutorComponent.get(appContext).backgroundExecutor()); 223 } 224 225 @WorkerThread updateDatabaseEntry( Context appContext, SpeedDialUiItem item, Channel channel)226 private static void updateDatabaseEntry( 227 Context appContext, SpeedDialUiItem item, Channel channel) { 228 Assert.isWorkerThread(); 229 SpeedDialEntry entry = 230 SpeedDialEntry.builder() 231 .setId(item.speedDialEntryId()) 232 .setContactId(item.contactId()) 233 .setLookupKey(item.lookupKey()) 234 .setDefaultChannel(channel) 235 .build(); 236 new SpeedDialEntryDatabaseHelper(appContext).update(ImmutableList.of(entry)); 237 } 238 } 239