A new way to add permissions to your extension

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!

8 Likes

This would help lots of people :+1: However I don’t know Java :frowning: I am not able to understand what is happening :sweat:

2 Likes

If you’d like to understand more of how I was thinking of developing this method, read here.

The App Inventor topic also provides you with a more finished and clean version at post #4.

3 Likes

Hi @hammerhai
I know Java a bit but I am unable to understand what is going on.
It would be really nice if you can tell me how it is different/better than the native method.

1 Like
form.askPermission(permissionId, new PermissionresultHandler() {
  @Override
  public void HandlePermissionResponse(String permission, boolean granted) {
    if (granted) {
      stateListener.onPermissionGranted();
    } else {
      form.dispatchPermissionDeniedEvent(me, "CallPermissionRequest", permissionId);
    }
  }
});

is longer than

CallPermissionRequest(new PermissionListener() {
  @Override
  public void onPermissionGranted() {
    READ_EXTERNAL_STORAGE = true;
    me.ReadFromFiles(files);
  }
}, "android.permission.READ_EXTERNAL_STORAGE");

and is just in general, easier to manage.

2 Likes

Maybe, but I will wait for MIT Team to add it to the sources.
Btw, this is how I use it:

1 Like

Yes, thank you!

3 Likes

Chile, anyways so I won’t be adding this to App Inventor because they still have things from 3 years ago to review and I’m not gonna be stuck on that list. Until then, I will continue working on more ways to make this better.

1 Like