• 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.precall.impl;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.support.annotation.MainThread;
22 import android.support.annotation.NonNull;
23 import android.support.annotation.Nullable;
24 import android.support.annotation.VisibleForTesting;
25 import android.telecom.PhoneAccount;
26 import android.telecom.PhoneAccountHandle;
27 import android.telecom.TelecomManager;
28 import android.telephony.PhoneNumberUtils;
29 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
30 import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
31 import com.android.contacts.common.widget.SelectPhoneAccountDialogOptions;
32 import com.android.dialer.callintent.CallIntentBuilder;
33 import com.android.dialer.common.Assert;
34 import com.android.dialer.common.LogUtil;
35 import com.android.dialer.configprovider.ConfigProviderComponent;
36 import com.android.dialer.logging.DialerImpression.Type;
37 import com.android.dialer.logging.Logger;
38 import com.android.dialer.precall.PreCallAction;
39 import com.android.dialer.precall.PreCallCoordinator;
40 import com.android.dialer.precall.PreCallCoordinator.PendingAction;
41 import com.android.dialer.preferredsim.PreferredAccountRecorder;
42 import com.android.dialer.preferredsim.PreferredAccountWorker;
43 import com.android.dialer.preferredsim.suggestion.SuggestionProvider;
44 import com.android.dialer.preferredsim.suggestion.SuggestionProvider.Suggestion;
45 import java.util.List;
46 import javax.inject.Inject;
47 
48 /** PreCallAction to select which phone account to call with. Ignored if there's only one account */
49 @SuppressWarnings("MissingPermission")
50 public class CallingAccountSelector implements PreCallAction {
51 
52   @VisibleForTesting static final String TAG_CALLING_ACCOUNT_SELECTOR = "CallingAccountSelector";
53 
54   private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
55 
56   private boolean isDiscarding;
57 
58   private final PreferredAccountWorker preferredAccountWorker;
59 
60   @Inject
CallingAccountSelector(PreferredAccountWorker preferredAccountWorker)61   CallingAccountSelector(PreferredAccountWorker preferredAccountWorker) {
62     this.preferredAccountWorker = preferredAccountWorker;
63   }
64 
65   @Override
requiresUi(Context context, CallIntentBuilder builder)66   public boolean requiresUi(Context context, CallIntentBuilder builder) {
67     if (!ConfigProviderComponent.get(context)
68         .getConfigProvider()
69         .getBoolean("precall_calling_account_selector_enabled", true)) {
70       return false;
71     }
72 
73     if (builder.getPhoneAccountHandle() != null) {
74       return false;
75     }
76     if (PhoneNumberUtils.isEmergencyNumber(builder.getUri().getSchemeSpecificPart())) {
77       return false;
78     }
79 
80     TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
81     List<PhoneAccountHandle> accounts = telecomManager.getCallCapablePhoneAccounts();
82     if (accounts.size() <= 1) {
83       return false;
84     }
85     return true;
86   }
87 
88   @Override
runWithoutUi(Context context, CallIntentBuilder builder)89   public void runWithoutUi(Context context, CallIntentBuilder builder) {
90     // do nothing.
91   }
92 
93   @Override
runWithUi(PreCallCoordinator coordinator)94   public void runWithUi(PreCallCoordinator coordinator) {
95     CallIntentBuilder builder = coordinator.getBuilder();
96     if (!requiresUi(coordinator.getActivity(), builder)) {
97       return;
98     }
99     switch (builder.getUri().getScheme()) {
100       case PhoneAccount.SCHEME_VOICEMAIL:
101         showDialog(
102             coordinator,
103             coordinator.startPendingAction(),
104             preferredAccountWorker.getVoicemailDialogOptions(),
105             null,
106             null,
107             null);
108         Logger.get(coordinator.getActivity()).logImpression(Type.DUAL_SIM_SELECTION_VOICEMAIL);
109         break;
110       case PhoneAccount.SCHEME_TEL:
111         processPreferredAccount(coordinator);
112         break;
113       default:
114         // might be PhoneAccount.SCHEME_SIP
115         LogUtil.e(
116             "CallingAccountSelector.run",
117             "unable to process scheme " + builder.getUri().getScheme());
118         break;
119     }
120   }
121 
122   /** Initiates a background worker to find if there's any preferred account. */
123   @MainThread
processPreferredAccount(PreCallCoordinator coordinator)124   private void processPreferredAccount(PreCallCoordinator coordinator) {
125     Assert.isMainThread();
126     CallIntentBuilder builder = coordinator.getBuilder();
127     Activity activity = coordinator.getActivity();
128     String phoneNumber = builder.getUri().getSchemeSpecificPart();
129     PendingAction pendingAction = coordinator.startPendingAction();
130 
131     coordinator.listen(
132         preferredAccountWorker.selectAccount(
133             phoneNumber,
134             activity.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts()),
135         result -> {
136           if (isDiscarding) {
137             // pendingAction is dropped by the coordinator before onDiscard is triggered.
138             return;
139           }
140           if (result.getSelectedPhoneAccountHandle().isPresent()) {
141 
142             if (result.getSuggestion().isPresent()
143                 && result
144                     .getSelectedPhoneAccountHandle()
145                     .get()
146                     .equals(result.getSuggestion().get().phoneAccountHandle)) {
147               builder
148                   .getInCallUiIntentExtras()
149                   .putString(
150                       SuggestionProvider.EXTRA_SIM_SUGGESTION_REASON,
151                       result.getSuggestion().get().reason.name());
152             }
153 
154             coordinator
155                 .getBuilder()
156                 .setPhoneAccountHandle(result.getSelectedPhoneAccountHandle().get());
157             pendingAction.finish();
158             return;
159           }
160           showDialog(
161               coordinator,
162               pendingAction,
163               result.getDialogOptionsBuilder().get().build(),
164               result.getDataId().orNull(),
165               phoneNumber,
166               result.getSuggestion().orNull());
167         },
168         (throwable) -> {
169           throw new RuntimeException(throwable);
170         });
171   }
172 
173   @MainThread
showDialog( PreCallCoordinator coordinator, PendingAction pendingAction, SelectPhoneAccountDialogOptions dialogOptions, @Nullable String dataId, @Nullable String number, @Nullable Suggestion suggestion)174   private void showDialog(
175       PreCallCoordinator coordinator,
176       PendingAction pendingAction,
177       SelectPhoneAccountDialogOptions dialogOptions,
178       @Nullable String dataId,
179       @Nullable String number,
180       @Nullable Suggestion suggestion) {
181     Assert.isMainThread();
182 
183     selectPhoneAccountDialogFragment =
184         SelectPhoneAccountDialogFragment.newInstance(
185             dialogOptions,
186             new SelectedListener(
187                 coordinator,
188                 pendingAction,
189                 new PreferredAccountRecorder(number, suggestion, dataId)));
190     selectPhoneAccountDialogFragment.show(
191         coordinator.getActivity().getFragmentManager(), TAG_CALLING_ACCOUNT_SELECTOR);
192   }
193 
194   @MainThread
195   @Override
onDiscard()196   public void onDiscard() {
197     isDiscarding = true;
198     if (selectPhoneAccountDialogFragment != null) {
199       selectPhoneAccountDialogFragment.dismiss();
200     }
201   }
202 
203   private class SelectedListener extends SelectPhoneAccountListener {
204 
205     private final PreCallCoordinator coordinator;
206     private final PreCallCoordinator.PendingAction listener;
207     private final PreferredAccountRecorder recorder;
208 
SelectedListener( @onNull PreCallCoordinator builder, @NonNull PreCallCoordinator.PendingAction listener, @NonNull PreferredAccountRecorder recorder)209     public SelectedListener(
210         @NonNull PreCallCoordinator builder,
211         @NonNull PreCallCoordinator.PendingAction listener,
212         @NonNull PreferredAccountRecorder recorder) {
213       this.coordinator = Assert.isNotNull(builder);
214       this.listener = Assert.isNotNull(listener);
215       this.recorder = Assert.isNotNull(recorder);
216     }
217 
218     @MainThread
219     @Override
onPhoneAccountSelected( PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId)220     public void onPhoneAccountSelected(
221         PhoneAccountHandle selectedAccountHandle, boolean setDefault, @Nullable String callId) {
222       coordinator.getBuilder().setPhoneAccountHandle(selectedAccountHandle);
223       recorder.record(coordinator.getActivity(), selectedAccountHandle, setDefault);
224       listener.finish();
225     }
226 
227     @MainThread
228     @Override
onDialogDismissed(@ullable String callId)229     public void onDialogDismissed(@Nullable String callId) {
230       if (isDiscarding) {
231         return;
232       }
233       coordinator.abortCall();
234       listener.finish();
235     }
236   }
237 }
238