AI2 Component  (Version nb184)
PhoneCall.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2019 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
9 import static android.Manifest.permission.CALL_PHONE;
10 import static android.Manifest.permission.PROCESS_OUTGOING_CALLS;
11 import static android.Manifest.permission.READ_CALL_LOG;
12 import static android.Manifest.permission.READ_PHONE_STATE;
13 
14 import android.content.BroadcastReceiver;
15 import android.content.Context;
16 import android.content.Intent;
17 import android.content.IntentFilter;
18 import android.net.Uri;
19 import android.telephony.TelephonyManager;
33 
61 @DesignerComponent(version = YaVersion.PHONECALL_COMPONENT_VERSION,
62  description = "<p>A non-visible component that makes a phone call to " +
63  "the number specified in the <code>PhoneNumber</code> property, which " +
64  "can be set either in the Designer or Blocks Editor. The component " +
65  "has a <code>MakePhoneCall</code> method, enabling the program to launch " +
66  "a phone call.</p>" +
67  "<p>Often, this component is used with the <code>ContactPicker</code> " +
68  "component, which lets the user select a contact from the ones stored " +
69  "on the phone and sets the <code>PhoneNumber</code> property to the " +
70  "contact's phone number.</p>" +
71  "<p>To directly specify the phone number (e.g., 650-555-1212), set " +
72  "the <code>PhoneNumber</code> property to a Text with the specified " +
73  "digits (e.g., \"6505551212\"). Dashes, dots, and parentheses may be " +
74  "included (e.g., \"(650)-555-1212\") but will be ignored; spaces may " +
75  "not be included.</p>",
76  category = ComponentCategory.SOCIAL,
77  nonVisible = true,
78  iconName = "images/phoneCall.png")
79 @SimpleObject
82 
86  private static final int PHONECALL_REQUEST_CODE = 0x50484F4E;
87  private String phoneNumber;
88  private final Context context;
89  private final CallStateReceiver callStateReceiver;
90  private boolean havePermission = false;
91  private boolean didRegisterReceiver = false;
92 
98  public PhoneCall(ComponentContainer container) {
99  super(container.$form());
100  context = container.$context();
102  form.registerForActivityResult(this, PHONECALL_REQUEST_CODE);
103  PhoneNumber("");
104  callStateReceiver = new CallStateReceiver();
105  }
106 
107  @SuppressWarnings({"unused"})
108  public void Initialize() {
109  if (form.doesAppDeclarePermission(READ_CALL_LOG)) {
110  form.askPermission(new BulkPermissionRequest(this, "Initialize",
111  PROCESS_OUTGOING_CALLS, READ_PHONE_STATE, READ_CALL_LOG) {
112  @Override
113  public void onGranted() {
114  registerCallStateMonitor();
115  }
116  });
117  }
118  }
119 
124  category = PropertyCategory.BEHAVIOR)
125  public String PhoneNumber() {
126  return phoneNumber;
127  }
128 
136  public void PhoneNumber(String phoneNumber) {
137  this.phoneNumber = phoneNumber;
138  }
139 
144  @SimpleFunction(description = "Launches the default dialer app set to start a phone call using"
145  + "the number in the PhoneNumber property.")
146  public void MakePhoneCall() {
147  Intent i = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel", this.phoneNumber, null));
148  if (i.resolveActivity(form.getPackageManager()) != null) {
149  form.startActivityForResult(i, PHONECALL_REQUEST_CODE);
150  }
151  }
152 
158  @UsesPermissions(CALL_PHONE)
159  @SimpleFunction(description = "Directly initiates a phone call using the number in the "
160  + "PhoneNumber property.")
161  public void MakePhoneCallDirect() {
162  // Check that we have permission and ask for it if we don't
163  if (!havePermission) {
164  form.askPermission(CALL_PHONE,
166  @Override
167  public void HandlePermissionResponse(String permission, boolean granted) {
168  if (granted) {
169  PhoneCall.this.havePermission = true;
171  } else {
172  form.dispatchPermissionDeniedEvent(PhoneCall.this, "MakePhoneCall", CALL_PHONE);
173  }
174  }
175  });
176  } else {
177  PhoneCallUtil.makePhoneCall(context, phoneNumber);
178  }
179  }
180 
190  @SimpleEvent(
191  description =
192  "Event indicating that a phonecall has started." +
193  " If status is 1, incoming call is ringing; " +
194  "if status is 2, outgoing call is dialled. " +
195  "phoneNumber is the incoming/outgoing phone number.")
196  @UsesPermissions({
197  PROCESS_OUTGOING_CALLS, // Deprecated SDK 29
198  READ_CALL_LOG, // minSDK 16, needed on SDK 29
199  READ_PHONE_STATE
200  })
201  public void PhoneCallStarted(int status, String phoneNumber) {
202  // invoke the application's "PhoneCallStarted" event handler.
203  EventDispatcher.dispatchEvent(this, "PhoneCallStarted", status, phoneNumber);
204  }
205 
217  @SimpleEvent(
218  description =
219  "Event indicating that a phone call has ended. " +
220  "If status is 1, incoming call is missed or rejected; " +
221  "if status is 2, incoming call is answered before hanging up; " +
222  "if status is 3, outgoing call is hung up. " +
223  "phoneNumber is the ended call phone number.")
224  @UsesPermissions({
225  PROCESS_OUTGOING_CALLS, // Deprecated SDK 29
226  READ_CALL_LOG, // minSDK 16, needed on SDK 29
227  READ_PHONE_STATE,
228  })
229  public void PhoneCallEnded(int status, String phoneNumber) {
230  // invoke the application's "PhoneCallEnded" event handler.
231  EventDispatcher.dispatchEvent(this, "PhoneCallEnded", status, phoneNumber);
232  }
233 
240  @SimpleEvent(
241  description =
242  "Event indicating that an incoming phone call is answered. " +
243  "phoneNumber is the incoming call phone number.")
244  @UsesPermissions({
245  PROCESS_OUTGOING_CALLS, // Deprecated SDK 29
246  READ_CALL_LOG, // minSDK 16, needed on SDK 29
247  READ_PHONE_STATE,
248  })
249  public void IncomingCallAnswered(String phoneNumber) {
250  // invoke the application's "IncomingCallAnswered" event handler.
251  EventDispatcher.dispatchEvent(this, "IncomingCallAnswered", phoneNumber);
252  }
253 
254  @Override
255  public void resultReturned(int requestCode, int resultCode, Intent data) {
256  if (requestCode == PHONECALL_REQUEST_CODE) {
257  PhoneCallStarted(2, "");
258  }
259  }
260 
265  private class CallStateReceiver extends BroadcastReceiver {
266  private int status; // 0:undetermined, 1:incoming ringed, 2:outgoing dialled,
267  // 3: incoming answered
268  private String number; // phone call number
269  public CallStateReceiver() {
270  status = 0;
271  number = "";
272  }
273 
274  @Override
275  public void onReceive(Context context, Intent intent) {
276  String action = intent.getAction();
277  if(TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)){
278  String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
279  if(TelephonyManager.EXTRA_STATE_RINGING.equals(state)){
280  // Incoming call rings
281  status = 1;
282  number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
283  if (number == null) {
284  // This gets called first with null, then with the actual number.
285  // Ignore the first invocation.
286  return;
287  }
288  PhoneCallStarted(1, number);
289  }else if(TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)){
290  // Call off-hook
291  if(status == 1){
292  // Incoming call answered
293  status = 3;
294  IncomingCallAnswered(number);
295  }
296  }else if(TelephonyManager.EXTRA_STATE_IDLE.equals(state)){
297  // Incomming/Outgoing Call ends
298  if(status == 1){
299  // Incoming Missed or Rejected
300  PhoneCallEnded(1, number);
301  }else if(status == 3){
302  // Incoming Answer Ended
303  PhoneCallEnded(2, number);
304  }else if(status == 2){
305  // Outgoing Ended
306  PhoneCallEnded(3, number);
307  }
308  status = 0;
309  number = "";
310  }
311  }else if(Intent.ACTION_NEW_OUTGOING_CALL.equals(action)){
312  // Outgoing call dialled
313  status = 2;
314  number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
315  PhoneCallStarted(2, number);
316  }
317  }
318  }
319 
323  private void registerCallStateMonitor(){
324  IntentFilter intentFilter = new IntentFilter();
325  intentFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
326  intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
327  context.registerReceiver(callStateReceiver, intentFilter);
328  didRegisterReceiver = true;
329  }
330 
334  private void unregisterCallStateMonitor() {
335  if (didRegisterReceiver) {
336  context.unregisterReceiver(callStateReceiver);
337  didRegisterReceiver = false;
338  }
339  }
340 
341  @Override
342  public void onDestroy() {
343  unregisterCallStateMonitor();
344  }
345 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.Form.askPermission
void askPermission(final String permission, final PermissionResultHandler responseRequestor)
Definition: Form.java:2633
com.google.appinventor.components.runtime.PhoneCall.PhoneCallStarted
void PhoneCallStarted(int status, String phoneNumber)
Definition: PhoneCall.java:201
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.PhoneCall.PhoneCall
PhoneCall(ComponentContainer container)
Definition: PhoneCall.java:98
com.google.appinventor.components.runtime.Form.registerForOnDestroy
void registerForOnDestroy(OnDestroyListener component)
Definition: Form.java:817
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.runtime.PhoneCall.PhoneNumber
void PhoneNumber(String phoneNumber)
Definition: PhoneCall.java:136
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.Form.dispatchPermissionDeniedEvent
void dispatchPermissionDeniedEvent(final Component component, final String functionName, final PermissionException exception)
Definition: Form.java:987
com.google.appinventor.components.runtime.PhoneCall.PhoneNumber
String PhoneNumber()
Definition: PhoneCall.java:125
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.PhoneCall.Initialize
void Initialize()
Definition: PhoneCall.java:108
com.google.appinventor.components.runtime.PermissionResultHandler
Definition: PermissionResultHandler.java:15
com.google.appinventor.components.runtime.PhoneCall.MakePhoneCallDirect
void MakePhoneCallDirect()
Definition: PhoneCall.java:161
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.util.BulkPermissionRequest
Definition: BulkPermissionRequest.java:22
com.google.appinventor.components.runtime.Form.doesAppDeclarePermission
boolean doesAppDeclarePermission(String permissionName)
Definition: Form.java:2730
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.PhoneCall
Definition: PhoneCall.java:80
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime.util.PhoneCallUtil
Definition: PhoneCallUtil.java:21
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.PhoneCall.IncomingCallAnswered
void IncomingCallAnswered(String phoneNumber)
Definition: PhoneCall.java:249
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.PhoneCall.onDestroy
void onDestroy()
Definition: PhoneCall.java:342
com.google.appinventor.components.runtime.ActivityResultListener
Definition: ActivityResultListener.java:16
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google
com
com.google.appinventor.components.runtime.PhoneCall.MakePhoneCall
void MakePhoneCall()
Definition: PhoneCall.java:146
com.google.appinventor.components.runtime.PhoneCall.PhoneCallEnded
void PhoneCallEnded(int status, String phoneNumber)
Definition: PhoneCall.java:229
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.PhoneCall.resultReturned
void resultReturned(int requestCode, int resultCode, Intent data)
Definition: PhoneCall.java:255
com.google.appinventor.components.runtime.util.PhoneCallUtil.makePhoneCall
static void makePhoneCall(Context context, String phoneNumber)
Definition: PhoneCallUtil.java:26
com.google.appinventor.components.runtime.AndroidNonvisibleComponent.form
final Form form
Definition: AndroidNonvisibleComponent.java:19
com.google.appinventor.components.runtime.Form.registerForActivityResult
int registerForActivityResult(ActivityResultListener listener)
Definition: Form.java:638
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.runtime.OnDestroyListener
Definition: OnDestroyListener.java:15
com.google.appinventor.components.annotations
com.google.appinventor