Before you start
Take note that I don’t know how to create a List with set values… only listVariable.add(item).
Summary
Today we’re going to take a look into how you as an extension developer, can better implement permission requests into your code. Let’s begin!
Template Code
To begin with, below is some template code for starters. You can find the untouched code at docs.kodular.io. Insert this code in a file named PermissionRequest.java.
// package of the extension will be "io.kodular.PermissionRequest"
package io.kodular;
// Only these imports are required to interact with Kodular
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.runtime.*;
import com.google.appinventor.components.common.*;
import android.content.Context;
import java.util.ArrayList;
import java.util.List;
import java.lang.String;
@DesignerComponent(
// You must update the version for each new release to upgrade your extension
version = 1,
description = "Extension template for simplified permission requests",
category = ComponentCategory.EXTENSION,
nonVisible = true,
// Change your extension's icon from here; can be a direct url
iconName = "images/extension.png"
)
@SimpleObject(external = true)
@UsesPermissions(permissionNames = "android.permission.READ_EXTERNAL_STORAGE, android.permission.WRITE_EXTERNAL_STORAGE")
public class PermissionRequest extends AndroidNonvisibleComponent {
private Context context;
private Form form;
// Class name
private PermissionRequest me;
public PermissionRequest(ComponentContainer container) {
super(container.$form());
this.context = container.$context();
this.form = container.$form();
me = this;
}
}
Creating your variables
Create your variable based off the end of “android.permission.X”. If your permission is “android.permission.WRITE_EXTERNAL_STORAGE” then add a variable named WRITE_EXTERNAL_STORAGE inside of the PermissionRequest class. This will control if the permission
has been granted or not. For an example, view below.
private boolean WRITE_EXTERNAL_STORAGE = false;
Creating an Interface
An interface is word for an Event Listener in Java. It will handle events like callbacks in JavaScript. Inside of the PermissionRequest class, we want to add this line of code…
interface PermissionListener {}
Creating a void inside of the PermissionListener
Great! We’ve created the Listener itself, now we have to make what happens when a permission is granted. Inside of the PermissionListener include this line of code…
public void onPermissionGranted();
Implementing the void that will ask for a permission
Inside of the PermissionRequest class, add these lines of code…
@SimpleFunction(description = "Create a new file with content UwU")
public void CreateFile(String content, String file) {}
Any code that asks for a permission must be a void and must NOT return a result otherwise this will not work.
Because this is an example, there will be no extended code inside of that void unless it’s related to events or permissions.
Adding a permission caller
Inside of the PermissionRequest class, add these lines of code…
public void CallPermissionRequest(final PermissionListener stateListener,
final String permissionId) {
form.askPermission(permissionId, new PermissionResultHandler() {
@Override
public void HandlePermissionResponse(String permission, boolean granted) {
if (granted) {
stateListener.onPermissionGranted();
} else {
form.dispatchPermissionDeniedEvent(me, "CallPermissionRequest", permissionId);
}
}
});
}
In the above code, we’ve referenced PermissionListener. permissionId will be the passed permission like “android.permission.WRITE_EXTERNAL_STORAGE” from the calling void.
Implementing the void that will ask for a permission (cont.)
Going back to the inside of CreateFile, add these lines of code…
if (WRITE_EXTERNAL_STORAGE) {
// Needed permission is granted, create the file
} else {
CallPermissionRequest(new PermissionListener() {
@Override
public void onPermissionGranted() {
WRITE_EXTERNAL_STORAGE = true;
me.CreateFile(content, file);
}
}, "android.permission.WRITE_EXTERNAL_STORAGE");
}
Good!!! Now we’ve completed the permission part with a void, but what about a block that you need to return a value for?
A block that returns a value of all types
First, since we cannot use this permission system with return statements, there is an alternative way. Inside of the PermissionRequest class, add these lines of code…
private boolean READ_EXTERNAL_STORAGE = false;
...
@SimpleEvent(description = "Multifunction return block.")
public void OnReturn(String block, List parameters, String val) {
EventDispatcher.dispatchEvent(this, "OnReturn", block, parameters, val);
}
@SimpleFunction(description = "Read content from a file UwU")
public void ReadFromFile(String file) {
// Add parameters to the list as they appear passed from left to right
List params = new Array<>();
params.add(file);
// Having a second parameter would go
// params.add(file2);
// Having a third parameter would go
// params.add(file3);
// And so on...
if (READ_EXTERNAL_STORAGE) {
// Needed permission is granted, read the file
OnReturn("ReadFromFile", params, String.valueOf("Special content"));
} else {
CallPermissionRequest(new PermissionListener() {
@Override
public void onPermissionGranted() {
READ_EXTERNAL_STORAGE = true;
me.ReadFromFile(file);
}
}, "android.permission.READ_EXTERNAL_STORAGE");
}
}
Always use String.valueOf(resultGoesHere) on the result value when passing it to OnReturn(…String val).
I want to return many values, what do I do
Modify the OnReturn block to to look like this…
@SimpleEvent(description = "Multifunction return block.")
public void OnReturn(String block, List parameters, List vals) {
EventDispatcher.dispatchEvent(this, "OnReturn", block, parameters, vals);
}
And use this as your example getter…
@SimpleFunction(description = "Read content from multiple files UwU")
public void ReadFromFiles(List files) {
// ^ Not sure if 'List files' is how it goes
if (READ_EXTERNAL_STORAGE) {
// Needed permission was granted...
List vals = new Array<>();
// Here type does not matter when adding vals...
// Add values as they are ready from your code like a for each statement
params.add(fileContent);
params.add(fileContent2);
params.add(fileContent3);
OnReturn("ReadFromFiles", files, vals);
} else {
CallPermissionRequest(new PermissionListener() {
@Override
public void onPermissionGranted() {
READ_EXTERNAL_STORAGE = true;
me.ReadFromFiles(files);
}
}, "android.permission.READ_EXTERNAL_STORAGE");
}
}
Of course if you want to get more experimental than you can however this is where the guide ends. I hope you liked it!