1 package org.robolectric.shadows;
2 
3 import static android.os.Build.VERSION_CODES.LOLLIPOP;
4 import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
5 import static android.os.Build.VERSION_CODES.M;
6 
7 import android.content.ComponentName;
8 import android.content.Context;
9 import android.net.Uri;
10 import android.os.Bundle;
11 import android.telecom.PhoneAccount;
12 import android.telecom.PhoneAccountHandle;
13 import android.telecom.TelecomManager;
14 import com.google.common.collect.ImmutableList;
15 import java.util.ArrayList;
16 import java.util.HashSet;
17 import java.util.LinkedHashMap;
18 import java.util.List;
19 import java.util.Set;
20 import org.robolectric.annotation.HiddenApi;
21 import org.robolectric.annotation.Implementation;
22 import org.robolectric.annotation.Implements;
23 import org.robolectric.annotation.RealObject;
24 import org.robolectric.util.ReflectionHelpers;
25 
26 @Implements(value = TelecomManager.class, minSdk = LOLLIPOP)
27 public class ShadowTelecomManager {
28 
29   @RealObject
30   private TelecomManager realObject;
31 
32   private PhoneAccountHandle simCallManager;
33   private LinkedHashMap<PhoneAccountHandle, PhoneAccount> accounts = new LinkedHashMap();
34   private List<CallRecord> incomingCalls = new ArrayList<>();
35   private List<CallRecord> unknownCalls = new ArrayList<>();
36   private String defaultDialerPackageName;
37   private boolean isInCall;
38 
39   @Implementation
getDefaultOutgoingPhoneAccount(String uriScheme)40   protected PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
41     return null;
42   }
43 
44   @Implementation
45   @HiddenApi
getUserSelectedOutgoingPhoneAccount()46   public PhoneAccountHandle getUserSelectedOutgoingPhoneAccount() {
47     return null;
48   }
49 
50   @Implementation
51   @HiddenApi
setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle)52   public void setUserSelectedOutgoingPhoneAccount(PhoneAccountHandle accountHandle) {
53   }
54 
55   @Implementation
getSimCallManager()56   protected PhoneAccountHandle getSimCallManager() {
57     return simCallManager;
58   }
59 
60   @Implementation(minSdk = M)
61   @HiddenApi
getSimCallManager(int userId)62   public PhoneAccountHandle getSimCallManager(int userId) {
63     return null;
64   }
65 
66   @Implementation
67   @HiddenApi
getConnectionManager()68   public PhoneAccountHandle getConnectionManager() {
69     return this.getSimCallManager();
70   }
71 
72   @Implementation
73   @HiddenApi
getPhoneAccountsSupportingScheme(String uriScheme)74   public List<PhoneAccountHandle> getPhoneAccountsSupportingScheme(String uriScheme) {
75     List<PhoneAccountHandle> result = new ArrayList<>();
76 
77     for (PhoneAccountHandle handle : accounts.keySet()) {
78       PhoneAccount phoneAccount = accounts.get(handle);
79       if(phoneAccount.getSupportedUriSchemes().contains(uriScheme)) {
80         result.add(handle);
81       }
82     }
83     return result;
84   }
85 
86   @Implementation(minSdk = M)
getCallCapablePhoneAccounts()87   protected List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
88     return this.getCallCapablePhoneAccounts(false);
89   }
90 
91   @Implementation(minSdk = M)
92   @HiddenApi
getCallCapablePhoneAccounts(boolean includeDisabledAccounts)93   public List<PhoneAccountHandle> getCallCapablePhoneAccounts(boolean includeDisabledAccounts) {
94     List<PhoneAccountHandle> result = new ArrayList<>();
95 
96     for (PhoneAccountHandle handle : accounts.keySet()) {
97       PhoneAccount phoneAccount = accounts.get(handle);
98       if(!phoneAccount.isEnabled() && !includeDisabledAccounts) {
99         continue;
100       }
101       result.add(handle);
102     }
103     return result;
104   }
105 
106   @Implementation
107   @HiddenApi
getPhoneAccountsForPackage()108   public List<PhoneAccountHandle> getPhoneAccountsForPackage() {
109     Context context = ReflectionHelpers.getField(realObject, "mContext");
110 
111     List<PhoneAccountHandle> results = new ArrayList<>();
112     for (PhoneAccountHandle handle : accounts.keySet()) {
113       if (handle.getComponentName().getPackageName().equals(context.getPackageName())) {
114         results.add(handle);
115       }
116     }
117     return results;
118   }
119 
120   @Implementation
getPhoneAccount(PhoneAccountHandle account)121   protected PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
122     return accounts.get(account);
123   }
124 
125   @Implementation
126   @HiddenApi
getAllPhoneAccountsCount()127   public int getAllPhoneAccountsCount() {
128     return accounts.size();
129   }
130 
131   @Implementation
132   @HiddenApi
getAllPhoneAccounts()133   public List<PhoneAccount> getAllPhoneAccounts() {
134     return ImmutableList.copyOf(accounts.values());
135   }
136 
137   @Implementation
138   @HiddenApi
getAllPhoneAccountHandles()139   public List<PhoneAccountHandle> getAllPhoneAccountHandles() {
140     return ImmutableList.copyOf(accounts.keySet());
141   }
142 
143   @Implementation
registerPhoneAccount(PhoneAccount account)144   protected void registerPhoneAccount(PhoneAccount account) {
145     accounts.put(account.getAccountHandle(), account);
146   }
147 
148   @Implementation
unregisterPhoneAccount(PhoneAccountHandle accountHandle)149   protected void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
150     accounts.remove(accountHandle);
151   }
152 
153   /** @deprecated */
154   @Deprecated
155   @Implementation
156   @HiddenApi
clearAccounts()157   public void clearAccounts() {
158     accounts.clear();
159   }
160 
161 
162   @Implementation(minSdk = LOLLIPOP_MR1)
163   @HiddenApi
clearAccountsForPackage(String packageName)164   public void clearAccountsForPackage(String packageName) {
165     Set<PhoneAccountHandle> phoneAccountHandlesInPackage = new HashSet<>();
166 
167     for (PhoneAccountHandle handle : accounts.keySet()) {
168       if (handle.getComponentName().getPackageName().equals(packageName)) {
169         phoneAccountHandlesInPackage.add(handle);
170       }
171     }
172 
173     for (PhoneAccountHandle handle : phoneAccountHandlesInPackage) {
174       accounts.remove(handle);
175     }
176   }
177 
178   /** @deprecated */
179   @Deprecated
180   @Implementation
181   @HiddenApi
getDefaultPhoneApp()182   public ComponentName getDefaultPhoneApp() {
183     return null;
184   }
185 
186   @Implementation(minSdk = M)
getDefaultDialerPackage()187   protected String getDefaultDialerPackage() {
188     return defaultDialerPackageName;
189   }
190 
191   @Implementation(minSdk = M)
192   @HiddenApi
setDefaultDialer(String packageName)193   public boolean setDefaultDialer(String packageName) {
194     this.defaultDialerPackageName = packageName;
195     return true;
196   }
197 
198   @Implementation(minSdk = M)
199   @HiddenApi
getSystemDialerPackage()200   public String getSystemDialerPackage() {
201     return null;
202   }
203 
204   @Implementation(minSdk = LOLLIPOP_MR1)
isVoiceMailNumber(PhoneAccountHandle accountHandle, String number)205   protected boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
206     return false;
207   }
208 
209   @Implementation(minSdk = M)
getVoiceMailNumber(PhoneAccountHandle accountHandle)210   protected String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
211     return null;
212   }
213 
214   @Implementation(minSdk = LOLLIPOP_MR1)
getLine1Number(PhoneAccountHandle accountHandle)215   protected String getLine1Number(PhoneAccountHandle accountHandle) {
216     return null;
217   }
218 
219   /** Sets the return value for {@link TelecomManager#isInCall}. */
setIsInCall(boolean isInCall)220   public void setIsInCall(boolean isInCall) {
221     this.isInCall = isInCall;
222   }
223 
224   /**
225    * Overrides behavior of {@link TelecomManager#isInCall} to return pre-set result.
226    *
227    * @return Value set by calling {@link ShadowTelecomManager#setIsInCall}. If setIsInCall has not
228    *     previously been called, will return false.
229    */
230   @Implementation
isInCall()231   protected boolean isInCall() {
232     return isInCall;
233   }
234 
235   @Implementation
236   @HiddenApi
getCallState()237   public int getCallState() {
238     return 0;
239   }
240 
241   @Implementation
242   @HiddenApi
isRinging()243   public boolean isRinging() {
244     for (CallRecord callRecord : incomingCalls) {
245       if (callRecord.isRinging) {
246         return true;
247       }
248     }
249     for (CallRecord callRecord : unknownCalls) {
250       if (callRecord.isRinging) {
251         return true;
252       }
253     }
254     return false;
255   }
256 
257   @Implementation
258   @HiddenApi
endCall()259   public boolean endCall() {
260     return false;
261   }
262 
263   @Implementation
acceptRingingCall()264   protected void acceptRingingCall() {}
265 
266   @Implementation
silenceRinger()267   protected void silenceRinger() {
268     for (CallRecord callRecord : incomingCalls) {
269       callRecord.isRinging = false;
270     }
271     for (CallRecord callRecord : unknownCalls) {
272       callRecord.isRinging = false;
273     }
274   }
275 
276   @Implementation
isTtySupported()277   protected boolean isTtySupported() {
278     return false;
279   }
280 
281   @Implementation
282   @HiddenApi
getCurrentTtyMode()283   public int getCurrentTtyMode() {
284     return 0;
285   }
286 
287   @Implementation
addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras)288   protected void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
289     incomingCalls.add(new CallRecord(phoneAccount, extras));
290   }
291 
getAllIncomingCalls()292   public List<CallRecord> getAllIncomingCalls() {
293     return incomingCalls;
294   }
295 
296   @Implementation
297   @HiddenApi
addNewUnknownCall(PhoneAccountHandle phoneAccount, Bundle extras)298   public void addNewUnknownCall(PhoneAccountHandle phoneAccount, Bundle extras) {
299     unknownCalls.add(new CallRecord(phoneAccount, extras));
300   }
301 
getAllUnknownCalls()302   public List<CallRecord> getAllUnknownCalls() {
303     return unknownCalls;
304   }
305 
306   @Implementation
handleMmi(String dialString)307   protected boolean handleMmi(String dialString) {
308     return false;
309   }
310 
311   @Implementation(minSdk = M)
handleMmi(String dialString, PhoneAccountHandle accountHandle)312   protected boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
313     return false;
314   }
315 
316   @Implementation(minSdk = LOLLIPOP_MR1)
getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle)317   protected Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
318     return Uri.parse("content://icc/adn");
319   }
320 
321   @Implementation
cancelMissedCallsNotification()322   protected void cancelMissedCallsNotification() {}
323 
324   @Implementation
showInCallScreen(boolean showDialpad)325   protected void showInCallScreen(boolean showDialpad) {}
326 
327   @Implementation(minSdk = M)
placeCall(Uri address, Bundle extras)328   protected void placeCall(Uri address, Bundle extras) {}
329 
330   @Implementation(minSdk = M)
331   @HiddenApi
enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled)332   public void enablePhoneAccount(PhoneAccountHandle handle, boolean isEnabled) {
333   }
334 
setSimCallManager(PhoneAccountHandle simCallManager)335   public void setSimCallManager(PhoneAccountHandle simCallManager) {
336     this.simCallManager = simCallManager;
337   }
338 
339   public static class CallRecord {
340     public final PhoneAccountHandle phoneAccount;
341     public final Bundle bundle;
342     private boolean isRinging = true;
343 
CallRecord(PhoneAccountHandle phoneAccount, Bundle extras)344     public CallRecord(PhoneAccountHandle phoneAccount, Bundle extras) {
345       this.phoneAccount = phoneAccount;
346       this.bundle = extras;
347     }
348   }
349 
350 }
351