Commit 5a74fe2e authored by Evan W. Patton's avatar Evan W. Patton Committed by Jeffrey Schiller

Make PhoneCall request READ_PHONE_STATE dangerous permission also

Change-Id: Ibacdc5f81f57ab6e49dc906daec47ae593fc94bc
parent e03ae81b
......@@ -69,6 +69,7 @@ import com.google.appinventor.components.runtime.util.JsonUtil;
import com.google.appinventor.components.runtime.util.MediaUtil;
import com.google.appinventor.components.runtime.util.OnInitializeListener;
import com.google.appinventor.components.runtime.util.PaintUtil;
import com.google.appinventor.components.runtime.util.BulkPermissionRequest;
import com.google.appinventor.components.runtime.util.ScreenDensityUtil;
import com.google.appinventor.components.runtime.util.SdkLevel;
import com.google.appinventor.components.runtime.util.ViewUtil;
......@@ -2533,6 +2534,53 @@ public class Form extends AppInventorCompatActivity
});
}
/**
* Evaluates the request for bulk permissions and asks the user for any ungranted permissions.
*
* @param request the request to evaluate
*/
public void askPermission(final BulkPermissionRequest request) {
final List<String> permissionsToAsk = request.getPermissions();
Iterator<String> it = permissionsToAsk.iterator();
while (it.hasNext()) {
if (!isDeniedPermission(it.next())) {
it.remove();
}
}
if (permissionsToAsk.size() == 0) {
// We already have all the necessary permissions
request.onGranted();
} else {
// Make sure we ask for permissions on the UI thread
androidUIHandler.post(new Runnable() {
@Override
public void run() {
final Iterator<String> it = permissionsToAsk.iterator();
final PermissionResultHandler handler = new PermissionResultHandler() {
final List<String> deniedPermissions = new ArrayList<String>();
@Override
public void HandlePermissionResponse(String permission, boolean granted) {
if (!granted) {
deniedPermissions.add(permission);
}
if (it.hasNext()) {
askPermission(it.next(), this);
} else {
if (deniedPermissions.size() == 0) {
request.onGranted();
} else {
request.onDenied(deniedPermissions.toArray(new String[] {}));
}
}
}
};
askPermission(it.next(), handler);
}
});
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
......
......@@ -19,8 +19,7 @@ import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.errors.PermissionException;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.BulkPermissionRequest;
import com.google.appinventor.components.runtime.util.PhoneCallUtil;
import android.Manifest;
......@@ -59,7 +58,6 @@ import android.telephony.TelephonyManager;
nonVisible = true,
iconName = "images/phoneCall.png")
@SimpleObject
@UsesPermissions(Manifest.permission.READ_PHONE_STATE)
public class PhoneCall extends AndroidNonvisibleComponent implements Component, OnDestroyListener, ActivityResultListener {
private static final String LOG_TAG = PhoneCall.class.getSimpleName();
......@@ -91,15 +89,11 @@ public class PhoneCall extends AndroidNonvisibleComponent implements Component,
@SuppressWarnings({"unused"})
public void Initialize() {
if (form.doesAppDeclarePermission(Manifest.permission.PROCESS_OUTGOING_CALLS)) {
form.askPermission(Manifest.permission.PROCESS_OUTGOING_CALLS, new PermissionResultHandler() {
form.askPermission(new BulkPermissionRequest(this, "Initialize",
Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_PHONE_STATE) {
@Override
public void HandlePermissionResponse(String permission, boolean granted) {
if (granted) {
registerCallStateMonitor();
} else {
form.dispatchPermissionDeniedEvent(PhoneCall.this, "Initialize",
Manifest.permission.PROCESS_OUTGOING_CALLS);
}
public void onGranted() {
registerCallStateMonitor();
}
});
}
......@@ -176,7 +170,7 @@ public class PhoneCall extends AndroidNonvisibleComponent implements Component,
" If status is 1, incoming call is ringing; " +
"if status is 2, outgoing call is dialled. " +
"phoneNumber is the incoming/outgoing phone number.")
@UsesPermissions(Manifest.permission.PROCESS_OUTGOING_CALLS)
@UsesPermissions({Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_PHONE_STATE})
public void PhoneCallStarted(int status, String phoneNumber) {
// invoke the application's "PhoneCallStarted" event handler.
EventDispatcher.dispatchEvent(this, "PhoneCallStarted", status, phoneNumber);
......@@ -196,7 +190,7 @@ public class PhoneCall extends AndroidNonvisibleComponent implements Component,
"if status is 2, incoming call is answered before hanging up; " +
"if status is 3, outgoing call is hung up. " +
"phoneNumber is the ended call phone number.")
@UsesPermissions(Manifest.permission.PROCESS_OUTGOING_CALLS)
@UsesPermissions({Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_PHONE_STATE})
public void PhoneCallEnded(int status, String phoneNumber) {
// invoke the application's "PhoneCallEnded" event handler.
EventDispatcher.dispatchEvent(this, "PhoneCallEnded", status, phoneNumber);
......@@ -211,7 +205,7 @@ public class PhoneCall extends AndroidNonvisibleComponent implements Component,
description =
"Event indicating that an incoming phone call is answered. " +
"phoneNumber is the incoming call phone number.")
@UsesPermissions(Manifest.permission.PROCESS_OUTGOING_CALLS)
@UsesPermissions({Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_PHONE_STATE})
public void IncomingCallAnswered(String phoneNumber) {
// invoke the application's "IncomingCallAnswered" event handler.
EventDispatcher.dispatchEvent(this, "IncomingCallAnswered", phoneNumber);
......
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2019 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.components.runtime.util;
import com.google.appinventor.components.runtime.Component;
import com.google.appinventor.components.runtime.Form;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Components can use BulkPermissionRequest to request multiple permissions in a single check
* by {@link Form#askPermission(BulkPermissionRequest)} rather than chaining multiple
* PermissionRequestCallbacks.
*
* @author ewpatton@mit.edu (Evan W. Patton)
*/
public abstract class BulkPermissionRequest {
private Component source;
private String caller;
private String[] permissions;
/**
* Construct a new BulkPermissionRequest.
*
* @param source the component requesting the permissions, for error reporting
* @param caller the method requesting the permissions, for error reporting
* @param permissions the list of permissions to request
*/
protected BulkPermissionRequest(Component source, String caller, String... permissions) {
this.source = source;
this.caller = caller;
this.permissions = permissions;
}
/**
* Subclasses must implement onGranted to provide behavior for when all permissions are granted.
* This method is called either because all permissions were previously granted or because the
* user was prompted and answered affirmatively.
*/
public abstract void onGranted();
/**
* Handles the scenario where one or more permissions in the request has been rejected. Subclasses
* may override this method to provide their own behavior.
*
* @param permissions the array of permissions that were denied
*/
public void onDenied(String[] permissions) {
Form form = (Form) source.getDispatchDelegate();
for (String permission : permissions) {
form.dispatchPermissionDeniedEvent(source, caller, permission);
}
}
/**
* Gets a mutable copy of the permissions requested.
*
* @return the list of desired permissions
*/
public final List<String> getPermissions() {
List<String> result = new ArrayList<>(permissions.length);
Collections.addAll(result, permissions);
return result;
}
}
......@@ -972,6 +972,7 @@ public abstract class ComponentProcessor extends AbstractProcessor {
for (String permission : usesPermissions.permissionNames().split(",")) {
updateWithNonEmptyValue(componentInfo.permissions, permission);
}
Collections.addAll(componentInfo.permissions, usesPermissions.value());
}
// Gather library names.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment