• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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