With every version of Android comes more security measures dropped into place. For example, in Android 6+ the user needs to grant permissions when doing certain activities, such as using the camera. These security measures are more aggressive than the previous form of asking permissions in the manifest file.
So what if we want to prompt the user to grant permissions at a time other than when trying to use the feature that requires the permissions? For example, asking for camera permissions when the application loads, rather than when we try to use the camera feature of the application?
We’re going to see how to request permissions in an Android NativeScript project with Angular.
If you’ve dabbled with Android development before, whether that be with Java or NativeScript, you’ll be familiar with the AndroidManifest.xml file that ships with every project. This is where you typically define the permissions your application requires. This does not change as you will need to define your permissions up front regardless of the Android version.
Our goal is to define where we want the second phase of permission granting to happen.
Take a look at the above animated image. In it we are requesting permission to use the camera even though we have no intention of actually using the camera.
So let’s create an example application!
For simplicity, we’re going to create a fresh NativeScript project that uses Angular. Assuming you have the NativeScript CLI installed and configured, execute the following:
tns create permissions-project --ng
The --ng
flag above indicates we are creating an Angular project rather than a vanilla NativeScript Core project.
Going forward, it is important to note that iOS doesn’t require permissions in the same sense that Android does. For this reason, this stuff only applies to Android. However, iOS will assume true on everything we design which will not hinder our development.
To request permission for things at runtime, we need a plugin for the job. Execute the following from the NativeScript CLI:
tns plugin add nativescript-permissions
With the project ready to go, we can now focus on the application.
When it comes to Android permissions, our first stop should be the AndroidManifest.xml file found in the project’s app/App_Resources/Android path.
By default, your manifest file might look something like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="__PACKAGE__"
android:versionCode="1"
android:versionName="1.0">
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="true"/>
<uses-sdk
android:minSdkVersion="17"
android:targetSdkVersion="__APILEVEL__"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name="com.tns.NativeScriptApplication"
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="com.tns.NativeScriptActivity"
android:label="@string/title_activity_kimera"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/LaunchScreenTheme">
<meta-data android:name="SET_THEME_ON_LAUNCH" android:resource="@style/AppTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.tns.ErrorReportActivity"/>
</application>
</manifest>
What we care about is the permissions section. As you can see, NativeScript already applies a few permissions by default:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
A full list of Android permissions can be found in the official Android documentation. We want to work with the camera permissions, so we can add the following to our list:
<uses-permission android:name="android.permission.CAMERA"/>
As a fun fact, not all Android permissions require a two step grant. This means that some permissions only need to go into the manifest and that is enough. They will never be presented to the user at runtime. The camera permissions do not fall under the one step scenario.
Now that the AndroidManifest.xml file is updated, we can focus on our application logic.
With a new NativeScript project comes a lot of boilerplate files and code that is overkill to what we’re after. We’re building a single page application, so we can strip out some things to prevent errors.
Open the project’s app/app.routing.ts file and make it look like the following:
import { NgModule } from "@angular/core";
import { NativeScriptRouterModule } from "nativescript-angular/router";
import { Routes } from "@angular/router";
const routes: Routes = [];
@NgModule({
imports: [NativeScriptRouterModule.forRoot(routes)],
exports: [NativeScriptRouterModule]
})
export class AppRoutingModule { }
We’ve removed references to the components that came with our project. Likewise, we need to do the same in the project’s app/app.module.ts file:
import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core";
import { NativeScriptModule } from "nativescript-angular/nativescript.module";
import { AppRoutingModule } from "./app.routing";
import { AppComponent } from "./app.component";
@NgModule({
bootstrap: [
AppComponent
],
imports: [
NativeScriptModule,
AppRoutingModule
],
declarations: [
AppComponent
],
providers: [],
schemas: [
NO_ERRORS_SCHEMA
]
})
export class AppModule { }
At this point we can focus our development on a singular page.
Open the project’s app/app.component.ts file and include the following TypeScript code. We’ll break it down after.
import { Component, OnInit } from "@angular/core";
import * as Permissions from "nativescript-permissions";
declare var android: any;
@Component({
selector: "ns-app",
templateUrl: "app.component.html",
})
export class AppComponent {
public getCameraPermission() {
Permissions.requestPermission(android.Manifest.permission.CAMERA, "Needed for connectivity status").then(() => {
console.log("Permission granted!");
}).catch(() => {
console.log("Permission is not granted (sadface)");
});
}
}
A few things to notice in the above code.
First we’re importing the plugin that we had previously installed. Because we need to use Android specific permissions, we need to interface directly with the Android SDK classes. At this time there are no TypeScript type definitions available for the entire Android SDK, which is why we declare them as a wildcard using:
declare var android: any;
Within the AppComponent
class we make use of the plugin, passing in the permission we want to request. Again, not all permissions need to be requested at runtime.
Permissions.requestPermission(android.Manifest.permission.CAMERA, "Needed for connectivity status").then(() => {
console.log("Permission granted!");
}).catch(() => {
console.log("Permission is not granted (sadface)");
});
If permissions were granted, the promise will resolve, otherwise it will reject.
It is important to note that it is necessary to always check if permissions were granted. The user can reject grants at any time via the Android OS settings.
Now let’s look at the UI. Open the project’s app/app.component.html file and include the following:
<ActionBar title="{N} Request Permissions"></ActionBar>
<StackLayout>
<Button text="Camera" class="btn btn-primary" (tap)="getCameraPermission()"></Button>
</StackLayout>
The UI simply calls our function when the user taps the button. Nothing fancy at all.
You just saw how to request Android permissions at runtime in a NativeScript with Angular application. This is not a replacement to the AndroidManifest.xml file, but an addition because newer versions of Android require the users to grant permissions at runtime as well.
Like previously mentioned, not all permissions require a request at runtime. For example, I had previously written a tutorial for checking the internet connection, which required a permission. This permission did not need to be granted at runtime.