Application Permission Management
Why permissions are required ?
Users must grant permission for an app to access sensitive information, including the current location, calendar, contact information, reminders and photos. Although people appreciate the convenience of using an app that has access to this information, they also expect to have control over their privacy.
Permission management requires OS-specific development since permission structures are totally separate in iOS and Android.
IOS
Smartface iOS Framework handles permissions automatically for the user. Developer does not need to handle permission management.
import Location from "@smartface/native/device/location";
Location.start("HIGH_ACCURACY", 1000);
Location.onLocationChanged = (event: {
latitude: number;
longitude: number;
}): void => {
console.log(
"Location latitude: " + event.latitude + " Longitude: " + event.longitude
);
};
In addition, there are also some convenient functions that check the status of related permission. Location permission status example like below;
import Location from "@smartface/native/device/location";
//@ts-ignore
switch (Location.ios.getAuthorizationStatus()) {
//@ts-ignore
case Location.ios.authorizationStatus.Authorized:
/*code block*/
break;
//@ts-ignore
case Location.ios.authorizationStatus.Denied:
/*code block*/
break;
//@ts-ignore
case Location.ios.authorizationStatus.NotDetermined:
/*code block*/
break;
default:
/*code block*/
}
Android
In the JavaScript side, there are some methods which are necessary for managing permissions.
import Permission from "@smartface/native/device/permission";
const CAMERA_PERMISSION_CODE: number = 1002;
const CAMERA_PERMISSION = Permissions.camera;
function getPermission(permission: string, code: number): void {
let prevPermissionRationale =
Permission.android.shouldShowRequestPermissionRationale(permission);
Permission.android.onRequestPermissionsResult = function (e) {
if (e.requestCode != code) return;
let currentPermissionRationale =
Permission.android.shouldShowRequestPermissionRationale(permission);
if (e.result) {
console.log("GRANTED");
} else if (!currentPermissionRationale) {
if (prevPermissionRationale) {
console.info("NEVER ASK AGAIN");
} else {
console.info("COULD NOT ASK");
}
} else {
console.log("DENIED");
}
};
if (Permission.android.checkPermission(permission)) {
console.log("ALREADY GRANTED");
} else {
Permission.android.requestPermissions(code, permission);
}
}
import Permission from "@smartface/native/device/permission";
import { Permissions } from "@smartface/native/device/permission/permission";
Permission.android.requestPermissions(Permissions.android.phone).then((e) => {
// your code
});
module.exports = pagePermission;
Sample examples of permissions
- Call Detection
- Camera & Location
- Contacts
import PgCallDetectionDesign from "generated/pages/pgCallDetection";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Router, Route } from "@smartface/router";
import Application from "@smartface/native/application";
import CallDetection from "@smartface/native/device/calldetection";
import Permission from "@smartface/native/device/permission";
import {
PermissionResult,
Permissions,
} from "@smartface/native/device/permission/permission";
export default class PgCallDetection extends withDismissAndBackButton(
PgCallDetectionDesign
) {
constructor(private router?: Router, private route?: Route) {
super({});
}
async initCallStateListener() {
const results = await Permission.android.requestPermissions(
Permissions.camera
);
if (results[0] === PermissionResult.GRANTED) {
CallDetection.on("callStateChanged", (a) => console.info(a));
}
}
onShow() {
super.onShow();
this.initBackButton(this.router); //Addes a back button to the page headerbar.
this.initCallStateListener();
}
onLoad() {
super.onLoad();
}
}
import pagePermissionDesign from "generated/pages/pagePermissions";
import { Router, Route } from "@smartface/router";
import Button from "@smartface/native/ui/button";
import Permission from "@smartface/native/device/permission";
import Application from "@smartface/native/application";
import Location from "@smartface/native/device/location";
import { Permissions } from "@smartface/native/device/permission/permission";
export default class pagePermissions extends pagePermissionDesign {
constructor(private router?: Router, private route?: Route) {
super({});
}
initPermission() {
this.btnCameraPermission.on("press", () => {
Permission.requestPermission(Permissions.camera)
.then((res) => alert("Permission Success: " + res))
.catch((err) => {
alert({
title: "Camera Permission", // title of the alert dialog
message: "Camere permission is required to use this app", // message of the alert dialog
});
console.error("Permission failed " + JSON.stringify(err));
});
});
this.btnLocationPermission.on("press", () => {
Permission.requestPermission(Permissions.location)
.then((res) => alert("Permission Success: " + res))
.catch((err) => {
alert({
title: "Location Permission", // title of the alert dialog
message: "Location permission is required to use this app", // message of the alert dialog
});
console.error("Permission failed " + JSON.stringify(err));
});
});
}
initLocation() {
this.btnGetLocation.on("press", () => {
Location.getCurrentLocation()
.then((res) => alert("Location Success: " + res))
.catch((err) => alert("Location failed " + JSON.stringify(err)));
});
}
onShow() {
super.onShow();
}
onLoad() {
super.onLoad();
this.initPermission();
this.initLocation();
}
}
import PgContactsDesign from "generated/pages/pgContacts";
import { withDismissAndBackButton } from "@smartface/mixins";
import { Router, Route } from "@smartface/router";
import Permission from "@smartface/native/device/permission";
import Contacts from "@smartface/native/device/contacts";
import Application from "@smartface/native/application";
import {
PermissionResult,
Permissions,
} from "@smartface/native/device/permission/permission";
export default class PgContacts extends withDismissAndBackButton(
PgContactsDesign
) {
constructor(private router?: Router, private route?: Route) {
super({});
}
getAllContacts() {
Permission.android
.requestPermissions(Permissions.android.contact)
.then((e) => {
Contacts.fetchAll({
onSuccess: (contacts) =>
console.info(contacts.map((contact) => Object.keys(contact))),
onFailure: (error) => console.error("fetchAll Failed", error),
});
});
}
async addContact() {
try {
await Permission.android.requestPermissions(Permissions.android.contact);
Contacts.add({
contact: new Contacts.Contact({
firstName: "Smartface",
lastName: "Mobile",
phoneNumbers: ["0000"],
}),
onSuccess: () => console.info("add Success"),
onFailure: () => console.error("add Failed"),
});
} catch (e) {
console.error(e.message, { stack: e.stack });
}
}
initializeButtons() {
this.btnFetchAll.on("press", () => this.getAllContacts());
this.btnAdd.on("press", () => this.addContact());
}
onShow() {
super.onShow();
this.initBackButton(this.router); //Addes a back button to the page headerbar.
}
onLoad() {
super.onLoad();
this.initializeButtons();
}
}
The permission prompt dialogs cannot be customized or localized. They are fully managed by the operating system for security purposes.
For required permission operations, you must add permissions to config/AndroidManifest.xml on Smartface IDE.
Common
We have common permissions for both iOS and Android. You can find the common permission list below.
Common Permissions |
---|
microphone |
storage |
camera |
location |
Example for microphone permission:
import Permission from "@smartface/native/device/permission";
import { Permissions } from "@smartface/native/device/permission/permission";
Permission.requestPermission(Permissions.microphone)
.then((res) => alert("Permission Success: " + res))
.catch((err) => {
alert({
title: "Microphone Permission",
message: "Microphone permission is required to use this app",
});
console.error("Permission failed " + JSON.stringify(err));
});
Which permission you should use with Smartface Native Framework?
Some features requires specific permission both iOS and Android. For example, when you want to access user's location, you need Location permission. In this section, you can find which Smartface Native Framework feature requires which permission.
Feature | Android Manifest | iOS Info.plist | Android Run-time |
---|---|---|---|
Application.byteReceived | READ_PHONE_STATE | android.phone | |
Application.byteSent | READ_PHONE_STATE | android.phone | |
Contacts.add | WRITE_CONTACTS | NSContactsUsageDescription | android.contact |
Contacts.getAll | READ_CONTACTS | NSContactsUsageDescription | android.contact |
Location.start() * | ACCESS_COARSE_LOCATION || ACCESS_FINE_LOCATION | NSLocationWhenInUseUsageDescription | location.approximate || location.precise |
Multimedia.pickMultipleFromGallery() | READ_EXTERNAL_STORAGE for API Level 32 and below | NSPhotoLibraryUsageDescription | storage on API Level 32 and below |
System.fingerPrintAvailable | USE_FINGERPRINT | - | |
IO.File ** | READ_EXTERNAL_STORAGE for API Level 32 and below | storage | |
Net.Http *** | ACCESS_NETWORK_STATE | NSAppTransportSecurity | - |
SpeechRecognizer.start | RECORD_AUDIO | NSSpeechRecognitionUsageDescription | android.microphone |
Device.Multimedia.startCamera | CAMERA | NSCameraUsageDescription | camera |
sf-extension-smsreceiver | RECEIVE_SMS | android.sms | |
Application.call | CALL_PHONE | android.phone | |
Device.System.Vibrate | VIBRATE | VIBRATE | |
UI.MapView | MAPS_RECEIVE | MAPS_RECEIVE |
* For Android, based on Provider; you can use ACCESS_COARSE_LOCATION for Location.Android.Provider.NETWORK, ACCESS_FINE_LOCATION for Location.Android.Provider.GPS and Location.Android.Provider.AUTO
** For Android, all methods and properties under IO.File requires some permissions if the file is not under Path.DataDirectory. Android 12L (API Level 32) and below requires READ_EXTERNAL_STORAGE, Android 13 (API Level 33) and above requires granular media permissions (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO)
*** For Android, all methods under Net.Http requires ACCESS_NETWORK_STATE permission.
It is sufficient to add only one to AndroidManifest in group permissions. For example, if you add READ_SMS permission, you do not need to add RECEIVE_SMS permissions.