Permalink
Browse files

Add blocks to assist in permission workflow

Change-Id: I39b3a6cc3b36653b2a78afcdb5ca04b70eb5c389
  • Loading branch information...
ewpatton authored and jisqyv committed Nov 16, 2018
1 parent 8c6d5df commit 2d19558acd16f2a3bbe1920215e3bfe4bca05675
Showing with 599 additions and 106 deletions.
  1. +16 −0 appinventor/appengine/src/com/google/appinventor/client/OdeMessages.java
  2. +7 −0 appinventor/appengine/src/com/google/appinventor/client/youngandroid/YoungAndroidFormUpgrader.java
  3. +5 −1 appinventor/blocklyeditor/src/versioning.js
  4. +39 −6 appinventor/buildserver/src/com/google/appinventor/buildserver/resources/runtime.scm
  5. +8 −2 appinventor/components/src/com/google/appinventor/components/common/YaVersion.java
  6. +3 −5 appinventor/components/src/com/google/appinventor/components/runtime/BarcodeScanner.java
  7. +3 −3 appinventor/components/src/com/google/appinventor/components/runtime/Camcorder.java
  8. +3 −3 appinventor/components/src/com/google/appinventor/components/runtime/Camera.java
  9. +6 −1 appinventor/components/src/com/google/appinventor/components/runtime/Canvas.java
  10. +3 −5 appinventor/components/src/com/google/appinventor/components/runtime/ContactPicker.java
  11. +16 −4 appinventor/components/src/com/google/appinventor/components/runtime/EmailPicker.java
  12. +23 −5 appinventor/components/src/com/google/appinventor/components/runtime/File.java
  13. +145 −0 appinventor/components/src/com/google/appinventor/components/runtime/Form.java
  14. +8 −1 appinventor/components/src/com/google/appinventor/components/runtime/ImagePicker.java
  15. +27 −14 appinventor/components/src/com/google/appinventor/components/runtime/LocationSensor.java
  16. +18 −5 appinventor/components/src/com/google/appinventor/components/runtime/PhoneCall.java
  17. +7 −1 appinventor/components/src/com/google/appinventor/components/runtime/Player.java
  18. +5 −0 appinventor/components/src/com/google/appinventor/components/runtime/ReplForm.java
  19. +4 −1 appinventor/components/src/com/google/appinventor/components/runtime/Sound.java
  20. +6 −3 appinventor/components/src/com/google/appinventor/components/runtime/SoundRecorder.java
  21. +9 −11 appinventor/components/src/com/google/appinventor/components/runtime/Texting.java
  22. +5 −1 appinventor/components/src/com/google/appinventor/components/runtime/VideoPlayer.java
  23. +28 −13 appinventor/components/src/com/google/appinventor/components/runtime/Web.java
  24. +16 −8 appinventor/components/src/com/google/appinventor/components/runtime/WebViewer.java
  25. +40 −0 appinventor/components/src/com/google/appinventor/components/runtime/errors/PermissionException.java
  26. +4 −1 appinventor/components/src/com/google/appinventor/components/runtime/util/ErrorMessages.java
  27. +82 −8 appinventor/components/src/com/google/appinventor/components/runtime/util/FileUtil.java
  28. +7 −1 appinventor/components/src/com/google/appinventor/components/runtime/util/FullScreenVideoUtil.java
  29. +43 −3 appinventor/components/src/com/google/appinventor/components/runtime/util/MediaUtil.java
  30. +13 −0 appinventor/docs/reference/components/userinterface.html
@@ -3928,6 +3928,10 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String functionNameParams();

@DefaultMessage("permissionName")
@Description("The name of the parameter that is used to report the name of a needed permission.")
String permissionNameParams();

@DefaultMessage("errorNumber")
@Description("")
String errorNumberParams();
@@ -4501,6 +4505,14 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String OtherScreenClosedEvents();

@DefaultMessage("PermissionDenied")
@Description("The name of the event handler for when the app is denied a dangerous permission by the user.")
String PermissionDeniedEvents();

@DefaultMessage("PermissionGranted")
@Description("The name of the event handler for when the app is granted a dangerous permission by the user.")
String PermissionGrantedEvents();

@DefaultMessage("ScreenOrientationChanged")
@Description("")
String ScreenOrientationChangedEvents();
@@ -5390,6 +5402,10 @@ String newerVersionComponentException(String componentType, int srcCompVersion,
@Description("")
String HideKeyboardMethods();

@DefaultMessage("AskForPermission")
@Description("")
String AskForPermissionMethods();

@DefaultMessage("Speak")
@Description("")
String SpeakMethods();
@@ -959,6 +959,13 @@ private static int upgradeFormProperties(Map<String, JSONValue> componentPropert
srcCompVersion = 23;
}

if (srcCompVersion < 24) {
// The AskForPermissions method was added.
// The PermissionDenied event was added.
// The PermissionGranted event was added.
srcCompVersion = 24;
}

return srcCompVersion;
}

@@ -2231,7 +2231,11 @@ Blockly.Versioning.AllUpgradeMaps =

// For FORM_COMPONENT_VERSION 23:
// - The ActionBar designer property was hidden and tied to the Theme property. No blocks need to be changed.
23: "noUpgrade"
23: "noUpgrade",

// For FORM_COMPONENT_VERSION 24:
// - The AskForPermissions method, PermissionDenied event, and PermissionGranted event were added. No blocks need to be changed.
24: "noUpgrade"


}, // End Screen
@@ -1,5 +1,5 @@
;;; Copyright 2009-2011 Google, All Rights reserved
;;; Copyright 2011-2013 MIT, All rights reserved
;;; Copyright 2011-2018 MIT, All rights reserved
;;; Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt

;;; These are the functions that define the YAIL (Young Android Intermediate Language) runtime They
@@ -436,6 +436,27 @@
(begin
(apply handler (gnu.lists.LList:makeList args 0))
#t)
;; PermissionException should be caught by a permissions-aware component and
;; handled correctly at the point it is caught. However, older extensions
;; might not be updated yet for SDK 23's dangerous permissions model, so if
;; an exception bubbles all the way up to here we can still catch and report
;; it. However, the best context we have for the PermissionDenied event is
;; that it occurred in the just-exited event handler code.
(exception com.google.appinventor.components.runtime.errors.PermissionException
(begin
(exception:printStackTrace)
;; Test to see if the event we are handling is the
;; PermissionDenied of the current form. If so, then we will
;; need to avoid re-invoking PermissionDenied.
(if (and (eq? (this) componentObject)
(equal? eventName "PermissionNeeded"))
;; Error is occurring in the PermissionDenied handler, so we
;; use the more general exception handler to prevent going
;; into an infinite loop.
(process-exception exception)
((this):PermissionDenied componentObject eventName
(exception:getPermissionNeeded)))
#f))
(exception java.lang.Throwable
(begin
(android-log-form (exception:getMessage))
@@ -888,6 +909,7 @@
(define-alias YailList <com.google.appinventor.components.runtime.util.YailList>)
(define-alias YailNumberToString <com.google.appinventor.components.runtime.util.YailNumberToString>)
(define-alias YailRuntimeError <com.google.appinventor.components.runtime.errors.YailRuntimeError>)
(define-alias PermissionException <com.google.appinventor.components.runtime.errors.PermissionException>)
(define-alias JavaJoinListOfStrings <com.google.appinventor.components.runtime.util.JavaJoinListOfStrings>)

(define-alias JavaCollection <java.util.Collection>)
@@ -956,10 +978,13 @@
(let ((coerced-args (coerce-args method-name arglist typelist)))
(let ((result
(if (all-coercible? coerced-args)
(apply invoke
`(,(lookup-in-current-form-environment component-name)
,method-name
,@coerced-args))
(try-catch
(apply invoke
`(,(lookup-in-current-form-environment component-name)
,method-name
,@coerced-args))
(exception PermissionException
(*:dispatchPermissionDeniedEvent (SimpleForm:getActiveForm) (lookup-in-current-form-environment component-name) method-name exception)))
(generate-runtime-type-error method-name arglist))))
;; TODO(markf): this should probably be generalized but for now this is OK, I think
(sanitize-component-data result))))
@@ -1158,7 +1183,10 @@
(let ((coerced-arg (coerce-arg property-value property-type)))
(android-log (format #f "coerced property value was: ~A " coerced-arg))
(if (all-coercible? (list coerced-arg))
(invoke comp prop-name coerced-arg)
(try-catch
(invoke comp prop-name coerced-arg)
(exception PermissionException
(*:dispatchPermissionDeniedEvent (SimpleForm:getActiveForm) comp prop-name exception)))
(generate-runtime-type-error prop-name (list property-value)))))


@@ -2617,6 +2645,11 @@ list, use the make-yail-list constructor with no arguments.
(try-catch
(list "OK"
(get-display-representation (force promise)))
(exception PermissionException
(exception:printStackTrace)
(list "NOK"
(string-append "Failed due to missing permission: "
(exception:getPermissionNeeded))))
(exception YailRuntimeError
(android-log (exception:getMessage))
(list "NOK"
@@ -431,8 +431,10 @@ private YaVersion() {
// - FUSIONTABLESCONTROL_COMPONENT_VERSION was incremented to 4
// For YOUNG_ANDROID_VERSION 172:
// - WEBVIEWER_COMPONENT_VERSION was incremented to 7
// For YOUNG_ANDROID_VERSION 173:
// - FORM_COMPONENT_VERSION was incremented to 24

public static final int YOUNG_ANDROID_VERSION = 172;
public static final int YOUNG_ANDROID_VERSION = 173;

// ............................... Blocks Language Version Number ...............................

@@ -748,7 +750,11 @@ private YaVersion() {
// - The Classic option for themes was added
// For FORM_COMPONENT_VERSION 23:
// - The ActionBar property was deprecated
public static final int FORM_COMPONENT_VERSION = 23;
// For FORM_COMPONENT_VERSION 24:
// - Added the AskForPermission method
// - Added the PermissionDenied event
// - Added the PermissionGranted event
public static final int FORM_COMPONENT_VERSION = 24;

// For FUSIONTABLESCONTROL_COMPONENT_VERSION 2:
// - The Fusiontables API was migrated from SQL to V1
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -105,10 +105,8 @@ public void HandlePermissionResponse(String permission, boolean granted) {
BarcodeScanner.this.havePermission = true;
DoScan();
} else {
BarcodeScanner.this
.container.$form()
.dispatchErrorOccurredEvent(BarcodeScanner.this, "BarcodeScanner",
ErrorMessages.ERROR_NO_CAMERA_PERMISSION, "");
form.dispatchPermissionDeniedEvent(BarcodeScanner.this, "DoScan",
Manifest.permission.CAMERA);
}
}
});
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -85,8 +85,8 @@ public void HandlePermissionResponse(String permission, boolean granted) {
me.havePermission = true;
me.RecordVideo();
} else {
form.dispatchErrorOccurredEvent(me, "Camcorder",
ErrorMessages.ERROR_NO_CAMERA_PERMISSION);
form.dispatchPermissionDeniedEvent(me, "RecordVideo",
Manifest.permission.CAMERA);
}
}
});
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -130,8 +130,8 @@ public void HandlePermissionResponse(String permission, boolean granted) {
me.havePermission = true;
me.TakePicture();
} else {
form.dispatchErrorOccurredEvent(me, "Camera",
ErrorMessages.ERROR_NO_CAMERA_PERMISSION);
form.dispatchPermissionDeniedEvent(me, "TakePicture",
Manifest.permission.CAMERA);
}
}
});
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -21,6 +21,7 @@
import com.google.appinventor.components.common.PropertyTypeConstants;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.collect.Sets;
import com.google.appinventor.components.runtime.errors.PermissionException;
import com.google.appinventor.components.runtime.util.BoundingBox;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.FileUtil;
@@ -1366,6 +1367,8 @@ public String Save() {
try {
File file = FileUtil.getPictureFile("png");
return saveFile(file, Bitmap.CompressFormat.PNG, "Save");
} catch (PermissionException e) {
container.$form().dispatchPermissionDeniedEvent(this, "Save", e);
} catch (IOException e) {
container.$form().dispatchErrorOccurredEvent(this, "Save",
ErrorMessages.ERROR_MEDIA_FILE_ERROR, e.getMessage());
@@ -1406,6 +1409,8 @@ public String SaveAs(String fileName) {
try {
File file = FileUtil.getExternalFile(fileName);
return saveFile(file, format, "SaveAs");
} catch (PermissionException e) {
container.$form().dispatchPermissionDeniedEvent(this, "SaveAs", e);
} catch (IOException e) {
container.$form().dispatchErrorOccurredEvent(this, "SaveAs",
ErrorMessages.ERROR_MEDIA_FILE_ERROR, e.getMessage());
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -122,10 +122,8 @@ public void HandlePermissionResponse(String permission, boolean granted) {
ContactPicker.this.havePermission = true;
ContactPicker.this.click();
} else {
ContactPicker
.this.container.$form()
.dispatchErrorOccurredEvent(ContactPicker.this, "ContactPicker",
ErrorMessages.ERROR_NO_READ_CONTACTS_PERMISSION, "");
container.$form().dispatchPermissionDeniedEvent(ContactPicker.this,
"Click", Manifest.permission.READ_CONTACTS);
}
}
});
@@ -1,6 +1,6 @@
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Copyright 2011-2018 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

@@ -12,9 +12,8 @@
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.ErrorMessages;
import com.google.appinventor.components.runtime.util.SdkLevel;

import android.Manifest;
import android.widget.AutoCompleteTextView;

/**
@@ -56,7 +55,20 @@
public EmailPicker(ComponentContainer container) {
super(container, new AutoCompleteTextView(container.$context()));
addressAdapter = new EmailAddressAdapter(container.$context());
((AutoCompleteTextView) super.view).setAdapter(addressAdapter);
}

@SuppressWarnings("unused") // Will be called from Scheme
public void Initialize() {
container.$form().askPermission(Manifest.permission.READ_CONTACTS, new PermissionResultHandler() {
@Override
public void HandlePermissionResponse(String permission, boolean granted) {
if (granted) {
((AutoCompleteTextView) view).setAdapter(addressAdapter);
} else {
container.$form().dispatchPermissionDeniedEvent(EmailPicker.this, "Initialize", permission);
}
}
});
}

/**
Oops, something went wrong.

0 comments on commit 2d19558

Please sign in to comment.