Commit 8d86c490 by Maurits van Beusekom

Migrate to FlutterPlugin API on Android

parent e3945eb1
## vNext
* Android: Migrate to FlutterPlugin Android API (better support for Add-to-App);
* Android: Suppress JAVA warnings which are generated to old platform code (only executes on older platforms).
## 4.3.0 ## 4.3.0
* Allow requesting location permissions when location services are disabled (on iOS this will redirect the user to the Location settings page); * Allow requesting location permissions when location services are disabled (on iOS this will redirect the user to the Location settings page);
......
...@@ -244,11 +244,21 @@ enum PermissionGroup { ...@@ -244,11 +244,21 @@ enum PermissionGroup {
/// Android: None /// Android: None
/// iOS: MPMediaLibrary /// iOS: MPMediaLibrary
mediaLibrary mediaLibrary,
/// Android: Check notification enable /// Android: Check notification enable
/// iOS: Check and request notification permission /// iOS: Check and request notification permission
notification notification,
/// Android Q: Check and request permissions to read from the media location (ACCESS_MEDIA_LOCATION)
/// Android pre-Q: Nothing
/// iOS: Nothing
accessMediaLocation,
/// Android Q: Check and request permissions to access the Activity Recognition API
/// Android pre-Q: Nothing
/// iOS: Nothing (should implement access to CMMotionActivity, see issue #219)
activityRecognition,
} }
``` ```
...@@ -261,9 +271,6 @@ enum PermissionStatus { ...@@ -261,9 +271,6 @@ enum PermissionStatus {
/// Permission to access the requested feature is denied by the user. /// Permission to access the requested feature is denied by the user.
denied, denied,
/// Permissions to access the feature is granted by the user but the feature is disabled.
disabled,
/// Permission to access the requested feature is granted by the user. /// Permission to access the requested feature is granted by the user.
granted, granted,
......
...@@ -32,6 +32,10 @@ android { ...@@ -32,6 +32,10 @@ android {
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
} }
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
} }
dependencies { dependencies {
......
package com.baseflow.permissionhandler;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
final class AppSettingsManager {
boolean openAppSettings(Context applicationContext) {
if (applicationContext == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
try {
Intent settingsIntent = new Intent();
settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
settingsIntent.setData(android.net.Uri.parse("package:" + applicationContext.getPackageName()));
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
applicationContext.startActivity(settingsIntent);
return true;
} catch (Exception ex) {
return false;
}
}
}
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import androidx.annotation.NonNull;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.Result;
import com.baseflow.permissionhandler.PermissionManager.ActivityRegistry;
import com.baseflow.permissionhandler.PermissionManager.PermissionRegistry;
import java.util.List;
final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
private final Context applicationContext;
private final Activity activity;
private final AppSettingsManager appSettingsManager;
private final PermissionManager permissionManager;
private final ServiceManager serviceManager;
private final ActivityRegistry activityRegistry;
private final PermissionRegistry permissionRegistry;
private final MethodChannel methodChannel;
MethodCallHandlerImpl(
Context applicationContext,
Activity activity,
BinaryMessenger messenger,
AppSettingsManager appSettingsManager,
PermissionManager permissionManager,
ServiceManager serviceManager,
ActivityRegistry activityRegistry,
PermissionRegistry permissionRegistry) {
this.applicationContext = applicationContext;
this.activity = activity;
this.appSettingsManager = appSettingsManager;
this.permissionManager = permissionManager;
this.serviceManager = serviceManager;
this.activityRegistry = activityRegistry;
this.permissionRegistry = permissionRegistry;
methodChannel = new MethodChannel(
messenger,
"flutter.baseflow.com/permissions/methods");
methodChannel.setMethodCallHandler(this);
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
{
switch (call.method) {
case "checkPermissionStatus": {
@PermissionConstants.PermissionGroup final int permission = (int) call.arguments;
@PermissionConstants.PermissionStatus final int permissionStatus =
permissionManager.checkPermissionStatus(
permission,
activity);
result.success(permissionStatus);
break;
}
case "checkServiceStatus": {
@PermissionConstants.PermissionGroup final int permission = (int) call.arguments;
@PermissionConstants.ServiceStatus final int serviceStatus =
serviceManager.checkServiceStatus(
permission,
activity);
result.success(serviceStatus);
break;
}
case "requestPermissions":
final List<Integer> permissions = call.arguments();
permissionManager.requestPermissions(
permissions,
activity,
activityRegistry,
permissionRegistry,
result::success,
(String errorCode, String errorDiscription) -> {
result.error(errorCode, errorDiscription, null);
});
break;
case "shouldShowRequestPermissionRationale": {
@PermissionConstants.PermissionGroup final int permission = (int) call.arguments;
final boolean showRationale = permissionManager
.shouldShowRequestPermissionRationale(permission, activity);
result.success(showRationale);
break;
}
case "openAppSettings":
boolean isOpen = appSettingsManager.openAppSettings(applicationContext);
result.success(isOpen);
break;
default:
result.notImplemented();
break;
}
}
void stopListening() {
methodChannel.setMethodCallHandler(null);
}
}
package com.baseflow.permissionhandler;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
final class PermissionConstants {
static final String LOG_TAG = "permissions_handler";
static final int PERMISSION_CODE = 24;
static final int PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS = 5672353;
//PERMISSION_GROUP
static final int PERMISSION_GROUP_CALENDAR = 0;
static final int PERMISSION_GROUP_CAMERA = 1;
static final int PERMISSION_GROUP_CONTACTS = 2;
static final int PERMISSION_GROUP_LOCATION = 3;
static final int PERMISSION_GROUP_LOCATION_ALWAYS = 4;
static final int PERMISSION_GROUP_LOCATION_WHEN_IN_USE = 5;
static final int PERMISSION_GROUP_MEDIA_LIBRARY = 6;
static final int PERMISSION_GROUP_MICROPHONE = 7;
static final int PERMISSION_GROUP_PHONE = 8;
static final int PERMISSION_GROUP_PHOTOS = 9;
static final int PERMISSION_GROUP_REMINDERS = 10;
static final int PERMISSION_GROUP_SENSORS = 11;
static final int PERMISSION_GROUP_SMS = 12;
static final int PERMISSION_GROUP_SPEECH = 13;
static final int PERMISSION_GROUP_STORAGE = 14;
static final int PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS = 15;
static final int PERMISSION_GROUP_NOTIFICATION = 16;
static final int PERMISSION_GROUP_ACCESS_MEDIA_LOCATION = 17;
static final int PERMISSION_GROUP_ACTIVITY_RECOGNITION = 18;
static final int PERMISSION_GROUP_UNKNOWN = 19;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_GROUP_CALENDAR,
PERMISSION_GROUP_CAMERA,
PERMISSION_GROUP_CONTACTS,
PERMISSION_GROUP_LOCATION,
PERMISSION_GROUP_LOCATION_ALWAYS,
PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
PERMISSION_GROUP_MEDIA_LIBRARY,
PERMISSION_GROUP_MICROPHONE,
PERMISSION_GROUP_PHONE,
PERMISSION_GROUP_PHOTOS,
PERMISSION_GROUP_REMINDERS,
PERMISSION_GROUP_SENSORS,
PERMISSION_GROUP_SMS,
PERMISSION_GROUP_SPEECH,
PERMISSION_GROUP_STORAGE,
PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS,
PERMISSION_GROUP_NOTIFICATION,
PERMISSION_GROUP_ACCESS_MEDIA_LOCATION,
PERMISSION_GROUP_ACTIVITY_RECOGNITION,
PERMISSION_GROUP_UNKNOWN,
})
@interface PermissionGroup {
}
//PERMISSION_STATUS
static final int PERMISSION_STATUS_DENIED = 0;
static final int PERMISSION_STATUS_GRANTED = 1;
static final int PERMISSION_STATUS_RESTRICTED = 2;
static final int PERMISSION_STATUS_UNKNOWN = 3;
static final int PERMISSION_STATUS_NEWER_ASK_AGAIN = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_STATUS_DENIED,
PERMISSION_STATUS_GRANTED,
PERMISSION_STATUS_RESTRICTED,
PERMISSION_STATUS_UNKNOWN,
PERMISSION_STATUS_NEWER_ASK_AGAIN,
})
@interface PermissionStatus {
}
//SERVICE_STATUS
static final int SERVICE_STATUS_DISABLED = 0;
static final int SERVICE_STATUS_ENABLED = 1;
static final int SERVICE_STATUS_NOT_APPLICABLE = 2;
static final int SERVICE_STATUS_UNKNOWN = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SERVICE_STATUS_DISABLED,
SERVICE_STATUS_ENABLED,
SERVICE_STATUS_NOT_APPLICABLE,
SERVICE_STATUS_UNKNOWN,
})
@interface ServiceStatus {
}
}
package com.baseflow.permissionhandler; package com.baseflow.permissionhandler;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.PowerManager;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi; import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import io.flutter.plugin.common.PluginRegistry.ActivityResultListener;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.IntDef; import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import androidx.core.app.ActivityCompat; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import androidx.core.content.ContextCompat;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
public class PermissionHandlerPlugin implements MethodCallHandler {
private static final String LOG_TAG = "permissions_handler";
private static final int PERMISSION_CODE = 24;
private static final int PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS = 5672353;
//PERMISSION_GROUP
private static final int PERMISSION_GROUP_CALENDAR = 0;
private static final int PERMISSION_GROUP_CAMERA = 1;
private static final int PERMISSION_GROUP_CONTACTS = 2;
private static final int PERMISSION_GROUP_LOCATION = 3;
private static final int PERMISSION_GROUP_LOCATION_ALWAYS = 4;
private static final int PERMISSION_GROUP_LOCATION_WHEN_IN_USE = 5;
private static final int PERMISSION_GROUP_MEDIA_LIBRARY = 6;
private static final int PERMISSION_GROUP_MICROPHONE = 7;
private static final int PERMISSION_GROUP_PHONE = 8;
private static final int PERMISSION_GROUP_PHOTOS = 9;
private static final int PERMISSION_GROUP_REMINDERS = 10;
private static final int PERMISSION_GROUP_SENSORS = 11;
private static final int PERMISSION_GROUP_SMS = 12;
private static final int PERMISSION_GROUP_SPEECH = 13;
private static final int PERMISSION_GROUP_STORAGE = 14;
private static final int PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS = 15;
private static final int PERMISSION_GROUP_NOTIFICATION = 16;
private static final int PERMISSION_GROUP_ACCESS_MEDIA_LOCATION = 17;
private static final int PERMISSION_GROUP_ACTIVITY_RECOGNITION = 18;
private static final int PERMISSION_GROUP_UNKNOWN = 19;
private PermissionHandlerPlugin(Registrar mRegistrar) {
this.mRegistrar = mRegistrar;
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_GROUP_CALENDAR,
PERMISSION_GROUP_CAMERA,
PERMISSION_GROUP_CONTACTS,
PERMISSION_GROUP_LOCATION,
PERMISSION_GROUP_LOCATION_ALWAYS,
PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
PERMISSION_GROUP_MEDIA_LIBRARY,
PERMISSION_GROUP_MICROPHONE,
PERMISSION_GROUP_PHONE,
PERMISSION_GROUP_PHOTOS,
PERMISSION_GROUP_REMINDERS,
PERMISSION_GROUP_SENSORS,
PERMISSION_GROUP_SMS,
PERMISSION_GROUP_SPEECH,
PERMISSION_GROUP_STORAGE,
PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS,
PERMISSION_GROUP_NOTIFICATION,
PERMISSION_GROUP_ACCESS_MEDIA_LOCATION,
PERMISSION_GROUP_ACTIVITY_RECOGNITION,
PERMISSION_GROUP_UNKNOWN,
})
private @interface PermissionGroup {
}
//PERMISSION_STATUS
private static final int PERMISSION_STATUS_DENIED = 0;
private static final int PERMISSION_STATUS_GRANTED = 1;
private static final int PERMISSION_STATUS_RESTRICTED = 2;
private static final int PERMISSION_STATUS_UNKNOWN = 3;
private static final int PERMISSION_STATUS_NEWER_ASK_AGAIN = 4;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PERMISSION_STATUS_DENIED,
PERMISSION_STATUS_GRANTED,
PERMISSION_STATUS_RESTRICTED,
PERMISSION_STATUS_UNKNOWN,
PERMISSION_STATUS_NEWER_ASK_AGAIN,
})
private @interface PermissionStatus {
}
//SERVICE_STATUS
private static final int SERVICE_STATUS_DISABLED = 0;
private static final int SERVICE_STATUS_ENABLED = 1;
private static final int SERVICE_STATUS_NOT_APPLICABLE = 2;
private static final int SERVICE_STATUS_UNKNOWN = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
SERVICE_STATUS_DISABLED,
SERVICE_STATUS_ENABLED,
SERVICE_STATUS_NOT_APPLICABLE,
SERVICE_STATUS_UNKNOWN,
})
private @interface ServiceStatus {
}
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import com.baseflow.permissionhandler.PermissionManager.ActivityRegistry;
import com.baseflow.permissionhandler.PermissionManager.PermissionRegistry;
/**
* Platform implementation of the permission_handler Flutter plugin.
*
* <p>Instantiate this in an add to app scenario to gracefully handle activity and context changes.
* See {@code com.example.permissionhandlerexample.MainActivity} for an example.
*
* <p>Call {@link #registerWith(Registrar)} to register an implementation of this that uses the
* stable {@code io.flutter.plugin.common} package.
*/
public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAware {
private @Nullable FlutterPluginBinding flutterPluginBinding;
private @Nullable MethodCallHandlerImpl methodCallHandler;
/**
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
* package.
*
* <p>Calling this automatically initializes the plugin. However plugins initialized this way
* won't react to changes in activity or context, unlike {@link PermissionHandlerPlugin}.
*/
public static void registerWith(Registrar registrar) { public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutter.baseflow.com/permissions/methods"); final PermissionHandlerPlugin permissionHandlerPlugin = new PermissionHandlerPlugin();
final PermissionHandlerPlugin permissionHandlerPlugin = new PermissionHandlerPlugin(registrar); permissionHandlerPlugin.startListening(
channel.setMethodCallHandler(permissionHandlerPlugin); registrar.context(),
registrar.activity(),
registrar.addRequestPermissionsResultListener(new PluginRegistry.RequestPermissionsResultListener() { registrar.messenger(),
@Override registrar::addActivityResultListener,
public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) { registrar::addRequestPermissionsResultListener
if (id == PERMISSION_CODE) { );
permissionHandlerPlugin.handlePermissionsRequest(permissions, grantResults);
return true;
} else {
return false;
} }
}
});
registrar.addActivityResultListener(new ActivityResultListener() {
@Override @Override
public boolean onActivityResult(int requestCode, int responseCode, Intent intent) { public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
if (requestCode == PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS) { this.flutterPluginBinding = binding;
permissionHandlerPlugin.handleIgnoreBatteryOptimizationsRequest(responseCode == Activity.RESULT_OK);
return true;
}
return false;
}
});
}
@PermissionGroup
private static int parseManifestName(String permission) {
switch (permission) {
case Manifest.permission.READ_CALENDAR:
case Manifest.permission.WRITE_CALENDAR:
return PERMISSION_GROUP_CALENDAR;
case Manifest.permission.CAMERA:
return PERMISSION_GROUP_CAMERA;
case Manifest.permission.READ_CONTACTS:
case Manifest.permission.WRITE_CONTACTS:
case Manifest.permission.GET_ACCOUNTS:
return PERMISSION_GROUP_CONTACTS;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
return PERMISSION_GROUP_LOCATION_ALWAYS;
case Manifest.permission.ACCESS_COARSE_LOCATION:
case Manifest.permission.ACCESS_FINE_LOCATION:
return PERMISSION_GROUP_LOCATION;
case Manifest.permission.RECORD_AUDIO:
return PERMISSION_GROUP_MICROPHONE;
case Manifest.permission.READ_PHONE_STATE:
case Manifest.permission.CALL_PHONE:
case Manifest.permission.READ_CALL_LOG:
case Manifest.permission.WRITE_CALL_LOG:
case Manifest.permission.ADD_VOICEMAIL:
case Manifest.permission.USE_SIP:
case Manifest.permission.BIND_CALL_REDIRECTION_SERVICE:
return PERMISSION_GROUP_PHONE;
case Manifest.permission.BODY_SENSORS:
return PERMISSION_GROUP_SENSORS;
case Manifest.permission.SEND_SMS:
case Manifest.permission.RECEIVE_SMS:
case Manifest.permission.READ_SMS:
case Manifest.permission.RECEIVE_WAP_PUSH:
case Manifest.permission.RECEIVE_MMS:
return PERMISSION_GROUP_SMS;
case Manifest.permission.READ_EXTERNAL_STORAGE:
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
return PERMISSION_GROUP_STORAGE;
case Manifest.permission.ACCESS_MEDIA_LOCATION:
return PERMISSION_GROUP_ACCESS_MEDIA_LOCATION;
case Manifest.permission.ACTIVITY_RECOGNITION:
return PERMISSION_GROUP_ACTIVITY_RECOGNITION;
default:
return PERMISSION_GROUP_UNKNOWN;
}
} }
private final Registrar mRegistrar;
private Result mResult;
private ArrayList<String> mRequestedPermissions;
@SuppressLint("UseSparseArrays")
private Map<Integer, Integer> mRequestResults = new HashMap<>();
@Override @Override
public void onMethodCall(MethodCall call, @NonNull Result result) { public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
switch (call.method) { this.flutterPluginBinding = null;
case "checkPermissionStatus": {
@PermissionGroup final int permission = (int) call.arguments;
@PermissionStatus final int permissionStatus = checkPermissionStatus(permission);
result.success(permissionStatus);
break;
}
case "checkServiceStatus": {
@PermissionGroup final int permission = (int) call.arguments;
@ServiceStatus final int serviceStatus = checkServiceStatus(permission);
result.success(serviceStatus);
break;
}
case "requestPermissions":
if (mResult != null) {
result.error(
"ERROR_ALREADY_REQUESTING_PERMISSIONS",
"A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time).",
null);
return;
}
mResult = result;
final List<Integer> permissions = call.arguments();
requestPermissions(permissions);
break;
case "shouldShowRequestPermissionRationale": {
@PermissionGroup final int permission = (int) call.arguments;
result.success(shouldShowRequestPermissionRationale(permission));
break;
}
case "openAppSettings":
boolean isOpen = openAppSettings();
result.success(isOpen);
break;
default:
result.notImplemented();
break;
}
}
@PermissionStatus
private int checkPermissionStatus(@PermissionGroup int permission) {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return PERMISSION_STATUS_UNKNOWN;
}
if (permission == PERMISSION_GROUP_NOTIFICATION) {
return checkNotificationPermissionStatus(context);
}
final List<String> names = getManifestNames(permission);
if (names == null) {
Log.d(LOG_TAG, "No android specific permissions needed for: " + permission);
return PERMISSION_STATUS_GRANTED;
}
//if no permissions were found then there is an issue and permission is not set in Android manifest
if (names.size() == 0) {
Log.d(LOG_TAG, "No permissions found in manifest for: " + permission);
return PERMISSION_STATUS_UNKNOWN;
}
final boolean targetsMOrHigher = context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.M;
for (String name : names) {
// Only handle them if the client app actually targets a API level greater than M.
if (targetsMOrHigher) {
if (permission == PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
String packageName = context.getPackageName();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
// PowerManager.isIgnoringBatteryOptimizations has been included in Android M first.
if (VERSION.SDK_INT >= VERSION_CODES.M) {
if (pm != null && pm.isIgnoringBatteryOptimizations(packageName)) {
return PERMISSION_STATUS_GRANTED;
} else {
return PERMISSION_STATUS_DENIED;
}
} else {
return PERMISSION_STATUS_RESTRICTED;
}
}
final int permissionStatus = ContextCompat.checkSelfPermission(context, name);
if (permissionStatus == PackageManager.PERMISSION_DENIED) {
if (VERSION.SDK_INT >= VERSION_CODES.M && isNeverAskAgainSelected(permission)) {
return PERMISSION_STATUS_NEWER_ASK_AGAIN;
} else return PERMISSION_STATUS_DENIED;
} else if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PERMISSION_STATUS_UNKNOWN;
}
}
}
return PERMISSION_STATUS_GRANTED;
}
@ServiceStatus
private int checkServiceStatus(int permission) {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return SERVICE_STATUS_UNKNOWN;
} }
if (permission == PERMISSION_GROUP_LOCATION || permission == PERMISSION_GROUP_LOCATION_ALWAYS || permission == PERMISSION_GROUP_LOCATION_WHEN_IN_USE) { @Override
return isLocationServiceEnabled(context) ? SERVICE_STATUS_ENABLED : SERVICE_STATUS_DISABLED; public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
} if(flutterPluginBinding == null) {
if (permission == PERMISSION_GROUP_PHONE) {
PackageManager pm = context.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return SERVICE_STATUS_NOT_APPLICABLE;
}
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null || telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
return SERVICE_STATUS_NOT_APPLICABLE;
}
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:123123"));
List<ResolveInfo> callAppsList = pm.queryIntentActivities(callIntent, 0);
if (callAppsList.isEmpty()) {
return SERVICE_STATUS_NOT_APPLICABLE;
}
if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
return SERVICE_STATUS_DISABLED;
}
return SERVICE_STATUS_ENABLED;
}
if (permission == PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? SERVICE_STATUS_ENABLED : SERVICE_STATUS_NOT_APPLICABLE;
}
return SERVICE_STATUS_NOT_APPLICABLE;
}
private boolean shouldShowRequestPermissionRationale(int permission) {
Activity activity = mRegistrar.activity();
if (activity == null) {
Log.d(LOG_TAG, "Unable to detect current Activity.");
return false;
}
List<String> names = getManifestNames(permission);
// if isn't an android specific group then go ahead and return false;
if (names == null) {
Log.d(LOG_TAG, "No android specific permissions needed for: " + permission);
return false;
}
if (names.isEmpty()) {
Log.d(LOG_TAG, "No permissions found in manifest for: " + permission + " no need to show request rationale");
return false;
}
//noinspection LoopStatementThatDoesntLoop
for (String name : names) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, name);
}
return false;
}
private void requestPermissions(List<Integer> permissions) {
if (mRegistrar.activity() == null) {
Log.d(LOG_TAG, "Unable to detect current Activity.");
for (Integer permission : permissions) {
mRequestResults.put(permission, PERMISSION_STATUS_UNKNOWN);
}
processResult();
return;
}
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) {
@PermissionStatus final int permissionStatus = checkPermissionStatus(permission);
if (permissionStatus != PERMISSION_STATUS_GRANTED) {
final List<String> names = getManifestNames(permission);
//check to see if we can find manifest names
//if we can't add as unknown and continue
if (names == null || names.isEmpty()) {
if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, PERMISSION_STATUS_UNKNOWN);
}
continue;
}
if (VERSION.SDK_INT >= VERSION_CODES.M && permission == PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
String packageName = mRegistrar.context().getPackageName();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
mRegistrar.activity().startActivityForResult(intent, PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS);
} else {
permissionsToRequest.addAll(names);
}
} else {
if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, PERMISSION_STATUS_GRANTED);
}
}
}
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
if (permissionsToRequest.size() > 0) {
ActivityCompat.requestPermissions(mRegistrar.activity(), requestPermissions, PERMISSION_CODE);
} else if (mRequestResults.size() > 0) {
processResult();
}
}
private void handlePermissionsRequest(String[] permissions, int[] grantResults) {
if (mResult == null) {
return;
}
for (int i = 0; i < permissions.length; i++) {
@PermissionGroup final int permission = parseManifestName(permissions[i]);
if (permission == PERMISSION_GROUP_UNKNOWN)
continue;
final int result = grantResults[i];
if (permission == PERMISSION_GROUP_MICROPHONE) {
if (!mRequestResults.containsKey(PERMISSION_GROUP_MICROPHONE)) {
mRequestResults.put(PERMISSION_GROUP_MICROPHONE, toPermissionStatus(permission, result));
}
if (!mRequestResults.containsKey(PERMISSION_GROUP_SPEECH)) {
mRequestResults.put(PERMISSION_GROUP_SPEECH, toPermissionStatus(permission, result));
}
} else if (permission == PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionStatus int permissionStatus = toPermissionStatus(permission, result);
if (!mRequestResults.containsKey(PERMISSION_GROUP_LOCATION_ALWAYS)) {
mRequestResults.put(PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
} else if (permission == PERMISSION_GROUP_LOCATION) {
@PermissionStatus int permissionStatus = toPermissionStatus(permission, result);
if (VERSION.SDK_INT < VERSION_CODES.Q) {
if (!mRequestResults.containsKey(PERMISSION_GROUP_LOCATION_ALWAYS)) {
mRequestResults.put(PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
}
if (!mRequestResults.containsKey(PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
mRequestResults.put(PERMISSION_GROUP_LOCATION_WHEN_IN_USE, permissionStatus);
}
mRequestResults.put(permission, permissionStatus);
} else if (!mRequestResults.containsKey(permission)) {
mRequestResults.put(permission, toPermissionStatus(permission, result));
}
updatePermissionShouldShowStatus(permission);
}
processResult();
}
private void handleIgnoreBatteryOptimizationsRequest(boolean granted) {
if (mResult == null) {
return; return;
} }
int status = granted ? PERMISSION_STATUS_GRANTED : PERMISSION_STATUS_DENIED; startListening(
flutterPluginBinding.getApplicationContext(),
mRequestResults.put(PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS, status); binding.getActivity(),
flutterPluginBinding.getBinaryMessenger(),
processResult(); binding::addActivityResultListener,
} binding::addRequestPermissionsResultListener);
@PermissionStatus
private int toPermissionStatus(@PermissionGroup int permission, int grantResult) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return VERSION.SDK_INT >= VERSION_CODES.M && isNeverAskAgainSelected(permission)
? PERMISSION_STATUS_NEWER_ASK_AGAIN
: PERMISSION_STATUS_DENIED;
}
return PERMISSION_STATUS_GRANTED;
}
private void processResult() {
mResult.success(mRequestResults);
mRequestResults.clear();
mResult = null;
}
private boolean openAppSettings() {
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
try {
Intent settingsIntent = new Intent();
settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
settingsIntent.setData(android.net.Uri.parse("package:" + context.getPackageName()));
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(settingsIntent);
return true;
} catch (Exception ex) {
return false;
}
}
private List<String> getManifestNames(@PermissionGroup int permission) {
final ArrayList<String> permissionNames = new ArrayList<>();
switch (permission) {
case PERMISSION_GROUP_CALENDAR:
if (hasPermissionInManifest(Manifest.permission.READ_CALENDAR))
permissionNames.add(Manifest.permission.READ_CALENDAR);
if (hasPermissionInManifest(Manifest.permission.WRITE_CALENDAR))
permissionNames.add(Manifest.permission.WRITE_CALENDAR);
break;
case PERMISSION_GROUP_CAMERA:
if (hasPermissionInManifest(Manifest.permission.CAMERA))
permissionNames.add(Manifest.permission.CAMERA);
break;
case PERMISSION_GROUP_CONTACTS:
if (hasPermissionInManifest(Manifest.permission.READ_CONTACTS))
permissionNames.add(Manifest.permission.READ_CONTACTS);
if (hasPermissionInManifest(Manifest.permission.WRITE_CONTACTS))
permissionNames.add(Manifest.permission.WRITE_CONTACTS);
if (hasPermissionInManifest(Manifest.permission.GET_ACCOUNTS))
permissionNames.add(Manifest.permission.GET_ACCOUNTS);
break;
case PERMISSION_GROUP_LOCATION_ALWAYS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (hasPermissionInManifest(Manifest.permission.ACCESS_BACKGROUND_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
case PERMISSION_GROUP_LOCATION_WHEN_IN_USE:
case PERMISSION_GROUP_LOCATION:
if (hasPermissionInManifest(Manifest.permission.ACCESS_COARSE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermissionInManifest(Manifest.permission.ACCESS_FINE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_FINE_LOCATION);
break;
case PERMISSION_GROUP_SPEECH:
case PERMISSION_GROUP_MICROPHONE:
if (hasPermissionInManifest(Manifest.permission.RECORD_AUDIO))
permissionNames.add(Manifest.permission.RECORD_AUDIO);
break;
case PERMISSION_GROUP_PHONE:
if (hasPermissionInManifest(Manifest.permission.READ_PHONE_STATE))
permissionNames.add(Manifest.permission.READ_PHONE_STATE);
if (hasPermissionInManifest(Manifest.permission.CALL_PHONE))
permissionNames.add(Manifest.permission.CALL_PHONE);
if (hasPermissionInManifest(Manifest.permission.READ_CALL_LOG))
permissionNames.add(Manifest.permission.READ_CALL_LOG);
if (hasPermissionInManifest(Manifest.permission.WRITE_CALL_LOG))
permissionNames.add(Manifest.permission.WRITE_CALL_LOG);
if (hasPermissionInManifest(Manifest.permission.ADD_VOICEMAIL))
permissionNames.add(Manifest.permission.ADD_VOICEMAIL);
if (hasPermissionInManifest(Manifest.permission.USE_SIP))
permissionNames.add(Manifest.permission.USE_SIP);
if (VERSION.SDK_INT >= VERSION_CODES.Q && hasPermissionInManifest(Manifest.permission.BIND_CALL_REDIRECTION_SERVICE))
permissionNames.add(Manifest.permission.BIND_CALL_REDIRECTION_SERVICE);
if (VERSION.SDK_INT >= VERSION_CODES.O && hasPermissionInManifest(Manifest.permission.ANSWER_PHONE_CALLS))
permissionNames.add(Manifest.permission.ANSWER_PHONE_CALLS);
break;
case PERMISSION_GROUP_SENSORS:
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT_WATCH) {
if (hasPermissionInManifest(Manifest.permission.BODY_SENSORS)) {
permissionNames.add(Manifest.permission.BODY_SENSORS);
}
}
break;
case PERMISSION_GROUP_SMS:
if (hasPermissionInManifest(Manifest.permission.SEND_SMS))
permissionNames.add(Manifest.permission.SEND_SMS);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_SMS))
permissionNames.add(Manifest.permission.RECEIVE_SMS);
if (hasPermissionInManifest(Manifest.permission.READ_SMS))
permissionNames.add(Manifest.permission.READ_SMS);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_WAP_PUSH))
permissionNames.add(Manifest.permission.RECEIVE_WAP_PUSH);
if (hasPermissionInManifest(Manifest.permission.RECEIVE_MMS))
permissionNames.add(Manifest.permission.RECEIVE_MMS);
break;
case PERMISSION_GROUP_STORAGE:
if (hasPermissionInManifest(Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (hasPermissionInManifest(Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
break;
case PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS:
if (VERSION.SDK_INT >= VERSION_CODES.M && hasPermissionInManifest(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
permissionNames.add(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
break;
case PERMISSION_GROUP_ACCESS_MEDIA_LOCATION:
if (VERSION.SDK_INT >= VERSION_CODES.Q && hasPermissionInManifest(Manifest.permission.ACCESS_MEDIA_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
break;
case PERMISSION_GROUP_ACTIVITY_RECOGNITION:
if (VERSION.SDK_INT >= VERSION_CODES.Q && hasPermissionInManifest(Manifest.permission.ACTIVITY_RECOGNITION))
permissionNames.add(Manifest.permission.ACTIVITY_RECOGNITION);
break;
case PERMISSION_GROUP_NOTIFICATION:
case PERMISSION_GROUP_MEDIA_LIBRARY:
case PERMISSION_GROUP_PHOTOS:
case PERMISSION_GROUP_REMINDERS:
case PERMISSION_GROUP_UNKNOWN:
return null;
}
return permissionNames;
} }
private boolean hasPermissionInManifest(String permission) { @Override
try { public void onDetachedFromActivityForConfigChanges() {
if (mRequestedPermissions != null) { onDetachedFromActivity();
for (String r : mRequestedPermissions) {
if (r.equals(permission)) {
return true;
}
}
}
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity();
if (context == null) {
Log.d(LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
if (info == null) {
Log.d(LOG_TAG, "Unable to get Package info, will not be able to determine permissions to request.");
return false;
}
mRequestedPermissions = new ArrayList<>(Arrays.asList(info.requestedPermissions));
for (String r : mRequestedPermissions) {
if (r.equals(permission)) {
return true;
}
}
} catch (Exception ex) {
Log.d(LOG_TAG, "Unable to check manifest for permission: ", ex);
}
return false;
} }
private void updatePermissionShouldShowStatus(@PermissionGroup int permission) { @Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
List<String> names = getManifestNames(permission); onAttachedToActivity(binding);
if (names == null || names.isEmpty()) {
return;
} }
final Context context = mRegistrar.activity() == null ? mRegistrar.activeContext() : mRegistrar.activity(); @Override
public void onDetachedFromActivity() {
if (context == null) { if (methodCallHandler == null) {
return; return;
} }
for (String name : names) { methodCallHandler.stopListening();
PermissionUtils.setRequestedPermission(context, name); methodCallHandler = null;
} }
}
private void startListening(
@RequiresApi(api = Build.VERSION_CODES.M) Context applicationContext,
private boolean isNeverAskAgainSelected(@PermissionGroup int permission) { Activity activity,
List<String> names = getManifestNames(permission); BinaryMessenger messenger,
ActivityRegistry activityRegistry,
if (names == null || names.isEmpty()) { PermissionRegistry permissionRegistry) {
return false; methodCallHandler = new MethodCallHandlerImpl(
} applicationContext,
activity,
final Activity activity = mRegistrar.activity(); messenger,
new AppSettingsManager(),
if (activity == null) { new PermissionManager(),
return false; new ServiceManager(),
} activityRegistry,
boolean isNeverAskAgainSelected = false; permissionRegistry);
for (String name : names) {
isNeverAskAgainSelected |= PermissionUtils.neverAskAgainSelected(activity, name);
}
return isNeverAskAgainSelected;
}
private boolean isLocationServiceEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
final LocationManager locationManager = context.getSystemService(LocationManager.class);
if (locationManager == null) {
return false;
}
return locationManager.isLocationEnabled();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final int locationMode;
try {
locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
return false;
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
} else {
final String locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
private int checkNotificationPermissionStatus(Context context) {
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
boolean isGranted = manager.areNotificationsEnabled();
if (isGranted) {
return PERMISSION_STATUS_GRANTED;
}
return PERMISSION_STATUS_DENIED;
} }
} }
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
final class PermissionManager {
interface ActivityRegistry {
void addListener(PluginRegistry.ActivityResultListener handler);
}
interface PermissionRegistry {
void addListener(PluginRegistry.RequestPermissionsResultListener handler);
}
interface ResultCallback {
void onResult(Map<Integer, Integer> results);
}
interface ErrorCallback {
void onError(String errorCode, String errorDescription);
}
private boolean ongoing = false;
@PermissionConstants.PermissionStatus
int checkPermissionStatus(
@PermissionConstants.PermissionGroup int permission,
Activity activity) {
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity or App Context.");
return PermissionConstants.PERMISSION_STATUS_UNKNOWN;
}
if (permission == PermissionConstants.PERMISSION_GROUP_NOTIFICATION) {
return checkNotificationPermissionStatus(activity);
}
final List<String> names = PermissionUtils.getManifestNames(activity, permission);
if (names == null) {
Log.d(PermissionConstants.LOG_TAG, "No android specific permissions needed for: " + permission);
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
//if no permissions were found then there is an issue and permission is not set in Android manifest
if (names.size() == 0) {
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + permission);
return PermissionConstants.PERMISSION_STATUS_UNKNOWN;
}
final boolean targetsMOrHigher = activity.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.M;
for (String name : names) {
// Only handle them if the client app actually targets a API level greater than M.
if (targetsMOrHigher) {
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
String packageName = activity.getPackageName();
PowerManager pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
// PowerManager.isIgnoringBatteryOptimizations has been included in Android M first.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (pm != null && pm.isIgnoringBatteryOptimizations(packageName)) {
return PermissionConstants.PERMISSION_STATUS_GRANTED;
} else {
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
} else {
return PermissionConstants.PERMISSION_STATUS_RESTRICTED;
}
}
final int permissionStatus = ContextCompat.checkSelfPermission(activity, name);
if (permissionStatus == PackageManager.PERMISSION_DENIED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
PermissionUtils.isNeverAskAgainSelected(activity, permission)) {
return PermissionConstants.PERMISSION_STATUS_NEWER_ASK_AGAIN;
} else return PermissionConstants.PERMISSION_STATUS_DENIED;
} else if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
return PermissionConstants.PERMISSION_STATUS_UNKNOWN;
}
}
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
void requestPermissions(
List<Integer> permissions,
Activity activity,
ActivityRegistry activityRegistry,
PermissionRegistry permissionRegistry,
ResultCallback resultCallback,
ErrorCallback errorCallback) {
if(ongoing) {
errorCallback.onError(
"ERROR_ALREADY_REQUESTING_PERMISSIONS",
"A request for permissions is already running, please wait for it to finish before doing another request (note that you can request multiple permissions at the same time).");
}
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
errorCallback.onError("ERROR_ANDROID_ACTIVITY_MISSING", "Unable to detect current Android Activity.");
return;
}
Map<Integer, Integer> requestResults = new HashMap<>();
ArrayList<String> permissionsToRequest = new ArrayList<>();
for (Integer permission : permissions) {
@PermissionConstants.PermissionStatus final int permissionStatus = checkPermissionStatus(permission, activity);
if (permissionStatus == PermissionConstants.PERMISSION_STATUS_GRANTED) {
if (!requestResults.containsKey(permission)) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_GRANTED);
}
continue;
}
final List<String> names = PermissionUtils.getManifestNames(activity, permission);
// check to see if we can find manifest names
// if we can't add as unknown and continue
if (names == null || names.isEmpty()) {
if (!requestResults.containsKey(permission)) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_UNKNOWN);
}
continue;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
activityRegistry.addListener(
new ActivityResultListener(resultCallback)
);
String packageName = activity.getPackageName();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
activity.startActivityForResult(intent, PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS);
} else {
permissionsToRequest.addAll(names);
}
}
final String[] requestPermissions = permissionsToRequest.toArray(new String[0]);
if (permissionsToRequest.size() > 0) {
permissionRegistry.addListener(
new RequestPermissionsListener(
activity,
requestResults,
(Map<Integer, Integer> results) -> {
ongoing = false;
resultCallback.onResult(results);
})
);
ongoing = true;
ActivityCompat.requestPermissions(
activity,
requestPermissions,
PermissionConstants.PERMISSION_CODE);
} else {
ongoing = false;
if (requestResults.size() > 0) {
resultCallback.onResult(requestResults);
}
}
}
boolean shouldShowRequestPermissionRationale(int permission, Activity activity) {
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
return false;
}
List<String> names = PermissionUtils.getManifestNames(activity, permission);
// if isn't an android specific group then go ahead and return false;
if (names == null) {
Log.d(PermissionConstants.LOG_TAG, "No android specific permissions needed for: " + permission);
return false;
}
if (names.isEmpty()) {
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + permission + " no need to show request rationale");
return false;
}
return ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0));
}
private int checkNotificationPermissionStatus(Context context) {
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
boolean isGranted = manager.areNotificationsEnabled();
if (isGranted) {
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
return PermissionConstants.PERMISSION_STATUS_DENIED;
}
@VisibleForTesting
static final class ActivityResultListener
implements PluginRegistry.ActivityResultListener {
// There's no way to unregister permission listeners in the v1 embedding, so we'll be called
// duplicate times in cases where the user denies and then grants a permission. Keep track of if
// we've responded before and bail out of handling the callback manually if this is a repeat
// call.
boolean alreadyCalled = false;
final ResultCallback callback;
@VisibleForTesting
ActivityResultListener(ResultCallback callback) {
this.callback = callback;
}
@Override
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
if(alreadyCalled || requestCode != PermissionConstants.PERMISSION_CODE_IGNORE_BATTERY_OPTIMIZATIONS) {
return false;
}
alreadyCalled = true;
final int status = resultCode == Activity.RESULT_OK
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
callback.onResult(new HashMap<>(PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS, status));
return true;
}
}
@VisibleForTesting
static final class RequestPermissionsListener
implements PluginRegistry.RequestPermissionsResultListener {
// There's no way to unregister permission listeners in the v1 embedding, so we'll be called
// duplicate times in cases where the user denies and then grants a permission. Keep track of if
// we've responded before and bail out of handling the callback manually if this is a repeat
// call.
boolean alreadyCalled = false;
final Activity activity;
final ResultCallback callback;
final Map<Integer, Integer> requestResults;
@VisibleForTesting
RequestPermissionsListener(
Activity activity,
Map<Integer, Integer> requestResults,
ResultCallback callback) {
this.activity = activity;
this.callback = callback;
this.requestResults = requestResults;
}
@Override
public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults)
{
if (alreadyCalled || id != PermissionConstants.PERMISSION_CODE) {
return false;
}
alreadyCalled = true;
for (int i = 0; i < permissions.length; i++) {
@PermissionConstants.PermissionGroup final int permission =
PermissionUtils.parseManifestName(permissions[i]);
if (permission == PermissionConstants.PERMISSION_GROUP_UNKNOWN)
continue;
final int result = grantResults[i];
if (permission == PermissionConstants.PERMISSION_GROUP_MICROPHONE) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_MICROPHONE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_MICROPHONE,
PermissionUtils.toPermissionStatus(this.activity, permission, result));
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_SPEECH)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_SPEECH,
PermissionUtils.toPermissionStatus(this.activity, permission, result));
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permission, result);
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS, permissionStatus);
}
} else if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION) {
@PermissionConstants.PermissionStatus int permissionStatus =
PermissionUtils.toPermissionStatus(this.activity, permission, result);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS,
permissionStatus);
}
}
if (!requestResults.containsKey(PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE)) {
requestResults.put(
PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE,
permissionStatus);
}
requestResults.put(permission, permissionStatus);
} else if (!requestResults.containsKey(permission)) {
requestResults.put(
permission,
PermissionUtils.toPermissionStatus(this.activity, permission, result));
}
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
}
this.callback.onResult(requestResults);
return true;
}
}
}
package com.baseflow.permissionhandler; package com.baseflow.permissionhandler;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class PermissionUtils { public class PermissionUtils {
@PermissionConstants.PermissionGroup
static int parseManifestName(String permission) {
switch (permission) {
case Manifest.permission.READ_CALENDAR:
case Manifest.permission.WRITE_CALENDAR:
return PermissionConstants.PERMISSION_GROUP_CALENDAR;
case Manifest.permission.CAMERA:
return PermissionConstants.PERMISSION_GROUP_CAMERA;
case Manifest.permission.READ_CONTACTS:
case Manifest.permission.WRITE_CONTACTS:
case Manifest.permission.GET_ACCOUNTS:
return PermissionConstants.PERMISSION_GROUP_CONTACTS;
case Manifest.permission.ACCESS_BACKGROUND_LOCATION:
return PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS;
case Manifest.permission.ACCESS_COARSE_LOCATION:
case Manifest.permission.ACCESS_FINE_LOCATION:
return PermissionConstants.PERMISSION_GROUP_LOCATION;
case Manifest.permission.RECORD_AUDIO:
return PermissionConstants.PERMISSION_GROUP_MICROPHONE;
case Manifest.permission.READ_PHONE_STATE:
case Manifest.permission.CALL_PHONE:
case Manifest.permission.READ_CALL_LOG:
case Manifest.permission.WRITE_CALL_LOG:
case Manifest.permission.ADD_VOICEMAIL:
case Manifest.permission.USE_SIP:
case Manifest.permission.BIND_CALL_REDIRECTION_SERVICE:
return PermissionConstants.PERMISSION_GROUP_PHONE;
case Manifest.permission.BODY_SENSORS:
return PermissionConstants.PERMISSION_GROUP_SENSORS;
case Manifest.permission.SEND_SMS:
case Manifest.permission.RECEIVE_SMS:
case Manifest.permission.READ_SMS:
case Manifest.permission.RECEIVE_WAP_PUSH:
case Manifest.permission.RECEIVE_MMS:
return PermissionConstants.PERMISSION_GROUP_SMS;
case Manifest.permission.READ_EXTERNAL_STORAGE:
case Manifest.permission.WRITE_EXTERNAL_STORAGE:
return PermissionConstants.PERMISSION_GROUP_STORAGE;
case Manifest.permission.ACCESS_MEDIA_LOCATION:
return PermissionConstants.PERMISSION_GROUP_ACCESS_MEDIA_LOCATION;
case Manifest.permission.ACTIVITY_RECOGNITION:
return PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION;
default:
return PermissionConstants.PERMISSION_GROUP_UNKNOWN;
}
}
static List<String> getManifestNames(Context context, @PermissionConstants.PermissionGroup int permission) {
final ArrayList<String> permissionNames = new ArrayList<>();
switch (permission) {
case PermissionConstants.PERMISSION_GROUP_CALENDAR:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CALENDAR))
permissionNames.add(Manifest.permission.READ_CALENDAR);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALENDAR))
permissionNames.add(Manifest.permission.WRITE_CALENDAR);
break;
case PermissionConstants.PERMISSION_GROUP_CAMERA:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.CAMERA))
permissionNames.add(Manifest.permission.CAMERA);
break;
case PermissionConstants.PERMISSION_GROUP_CONTACTS:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CONTACTS))
permissionNames.add(Manifest.permission.READ_CONTACTS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CONTACTS))
permissionNames.add(Manifest.permission.WRITE_CONTACTS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.GET_ACCOUNTS))
permissionNames.add(Manifest.permission.GET_ACCOUNTS);
break;
case PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_BACKGROUND_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
}
case PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE:
case PermissionConstants.PERMISSION_GROUP_LOCATION:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_COARSE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_COARSE_LOCATION);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_FINE_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_FINE_LOCATION);
break;
case PermissionConstants.PERMISSION_GROUP_SPEECH:
case PermissionConstants.PERMISSION_GROUP_MICROPHONE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECORD_AUDIO))
permissionNames.add(Manifest.permission.RECORD_AUDIO);
break;
case PermissionConstants.PERMISSION_GROUP_PHONE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_PHONE_STATE))
permissionNames.add(Manifest.permission.READ_PHONE_STATE);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.CALL_PHONE))
permissionNames.add(Manifest.permission.CALL_PHONE);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_CALL_LOG))
permissionNames.add(Manifest.permission.READ_CALL_LOG);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_CALL_LOG))
permissionNames.add(Manifest.permission.WRITE_CALL_LOG);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.ADD_VOICEMAIL))
permissionNames.add(Manifest.permission.ADD_VOICEMAIL);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.USE_SIP))
permissionNames.add(Manifest.permission.USE_SIP);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasPermissionInManifest(context, permissionNames, Manifest.permission.BIND_CALL_REDIRECTION_SERVICE))
permissionNames.add(Manifest.permission.BIND_CALL_REDIRECTION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasPermissionInManifest(context, permissionNames, Manifest.permission.ANSWER_PHONE_CALLS))
permissionNames.add(Manifest.permission.ANSWER_PHONE_CALLS);
break;
case PermissionConstants.PERMISSION_GROUP_SENSORS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.BODY_SENSORS)) {
permissionNames.add(Manifest.permission.BODY_SENSORS);
}
}
break;
case PermissionConstants.PERMISSION_GROUP_SMS:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.SEND_SMS))
permissionNames.add(Manifest.permission.SEND_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_SMS))
permissionNames.add(Manifest.permission.RECEIVE_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_SMS))
permissionNames.add(Manifest.permission.READ_SMS);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_WAP_PUSH))
permissionNames.add(Manifest.permission.RECEIVE_WAP_PUSH);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.RECEIVE_MMS))
permissionNames.add(Manifest.permission.RECEIVE_MMS);
break;
case PermissionConstants.PERMISSION_GROUP_STORAGE:
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.READ_EXTERNAL_STORAGE);
if (hasPermissionInManifest(context, permissionNames, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionNames.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
break;
case PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && hasPermissionInManifest(context, permissionNames, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS))
permissionNames.add(Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
break;
case PermissionConstants.PERMISSION_GROUP_ACCESS_MEDIA_LOCATION:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasPermissionInManifest(context, permissionNames, Manifest.permission.ACCESS_MEDIA_LOCATION))
permissionNames.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
break;
case PermissionConstants.PERMISSION_GROUP_ACTIVITY_RECOGNITION:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && hasPermissionInManifest(context, permissionNames, Manifest.permission.ACTIVITY_RECOGNITION))
permissionNames.add(Manifest.permission.ACTIVITY_RECOGNITION);
break;
case PermissionConstants.PERMISSION_GROUP_NOTIFICATION:
case PermissionConstants.PERMISSION_GROUP_MEDIA_LIBRARY:
case PermissionConstants.PERMISSION_GROUP_PHOTOS:
case PermissionConstants.PERMISSION_GROUP_REMINDERS:
case PermissionConstants.PERMISSION_GROUP_UNKNOWN:
return null;
}
return permissionNames;
}
private static boolean hasPermissionInManifest(Context context, ArrayList<String> confirmedPermissions, String permission) {
try {
if (confirmedPermissions != null) {
for (String r : confirmedPermissions) {
if (r.equals(permission)) {
return true;
}
}
}
if (context == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity or App Context.");
return false;
}
PackageInfo info = context
.getPackageManager()
.getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
if (info == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to get Package info, will not be able to determine permissions to request.");
return false;
}
confirmedPermissions = new ArrayList<>(Arrays.asList(info.requestedPermissions));
for (String r : confirmedPermissions) {
if (r.equals(permission)) {
return true;
}
}
} catch (Exception ex) {
Log.d(PermissionConstants.LOG_TAG, "Unable to check manifest for permission: ", ex);
}
return false;
}
@PermissionConstants.PermissionStatus
static int toPermissionStatus(final Activity activity, @PermissionConstants.PermissionGroup int permission, int grantResult) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && PermissionUtils.isNeverAskAgainSelected(activity, permission)
? PermissionConstants.PERMISSION_STATUS_NEWER_ASK_AGAIN
: PermissionConstants.PERMISSION_STATUS_DENIED;
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
}
static void updatePermissionShouldShowStatus(final Activity activity, @PermissionConstants.PermissionGroup int permission) {
if (activity == null) {
return;
}
List<String> names = getManifestNames(activity, permission);
if (names == null || names.isEmpty()) {
return;
}
for (String name : names) {
PermissionUtils.setRequestedPermission(activity, name);
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
static boolean isNeverAskAgainSelected(final Activity activity, @PermissionConstants.PermissionGroup int permission) {
if (activity == null) {
return false;
}
List<String> names = getManifestNames(activity, permission);
if (names == null || names.isEmpty()) {
return false;
}
boolean isNeverAskAgainSelected = false;
for (String name : names) {
isNeverAskAgainSelected |= PermissionUtils.neverAskAgainSelected(activity, name);
}
return isNeverAskAgainSelected;
}
@RequiresApi(api = Build.VERSION_CODES.M) @RequiresApi(api = Build.VERSION_CODES.M)
static boolean neverAskAgainSelected(final Activity activity, final String permission) { static boolean neverAskAgainSelected(final Activity activity, final String permission) {
final boolean hasRequestedPermissionBefore = getRequestedPermissionBefore(activity, permission); final boolean hasRequestedPermissionBefore = getRequestedPermissionBefore(activity, permission);
......
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import java.util.List;
final class ServiceManager {
@PermissionConstants.ServiceStatus
int checkServiceStatus(
int permission,
Activity activity) {
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity or App Context.");
return PermissionConstants.SERVICE_STATUS_UNKNOWN;
}
if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
return isLocationServiceEnabled(activity)
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_DISABLED;
}
if (permission == PermissionConstants.PERMISSION_GROUP_PHONE) {
PackageManager pm = activity.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
}
TelephonyManager telephonyManager = (TelephonyManager) activity
.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null || telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
}
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:123123"));
List<ResolveInfo> callAppsList = pm.queryIntentActivities(callIntent, 0);
if (callAppsList.isEmpty()) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
}
if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
return PermissionConstants.SERVICE_STATUS_DISABLED;
}
return PermissionConstants.SERVICE_STATUS_ENABLED;
}
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
}
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
}
private boolean isLocationServiceEnabled(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
final LocationManager locationManager = context.getSystemService(LocationManager.class);
if (locationManager == null) {
return false;
}
return locationManager.isLocationEnabled();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return isLocationServiceEnablePrePie(context);
} else {
return isLocationServiceEnablePreKitKat(context);
}
}
// Suppress deprecation warnings since it's purpose is to support to be backwards compatible with
// pre Pie versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnablePrePie(Context context)
{
if (VERSION.SDK_INT < VERSION_CODES.P)
return false;
final int locationMode;
try {
locationMode = Settings.Secure.getInt(
context.getContentResolver(),
Settings.Secure.LOCATION_MODE);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
return false;
}
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
}
// Suppress deprecation warnings since it's purpose is to support to be backwards compatible with
// pre KitKat versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnablePreKitKat(Context context)
{
if (VERSION.SDK_INT >= VERSION_CODES.KITKAT)
return false;
final String locationProviders = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
return !TextUtils.isEmpty(locationProviders);
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.permissionhandlerexample">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Permissions options for the `contacts` group -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<!-- Permissions options for the `storage` group -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Permissions options for the `camera` group -->
<uses-permission android:name="android.permission.CAMERA"/>
<!-- Permissions options for the `sms` group -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_WAP_PUSH"/>
<uses-permission android:name="android.permission.RECEIVE_MMS"/>
<!-- Permissions options for the `phone` group -->
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.ADD_VOICEMAIL"/>
<uses-permission android:name="android.permission.USE_SIP"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.WRITE_CALL_LOG"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<!-- Permissions options for the `calendar` group -->
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<!-- Permissions options for the `location` group -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Permissions options for the `microphone` or `speech` group -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Permissions options for the `access_media_location` group -->
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
</manifest>
...@@ -51,28 +51,30 @@ ...@@ -51,28 +51,30 @@
<!-- Permissions options for the `sensors` group --> <!-- Permissions options for the `sensors` group -->
<uses-permission android:name="android.permission.BODY_SENSORS" /> <uses-permission android:name="android.permission.BODY_SENSORS" />
<!-- Permissions options for the `access_media_location` group --> <!-- Permissions options for the `accessMediaLocation` group -->
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
<!-- Permissions options for the `activityRecognition` group -->
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<application <application
android:name="io.flutter.app.FlutterApplication" android:name="io.flutter.app.FlutterApplication"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="permission_handler_example" android:label="permission_handler_example"
tools:ignore="AllowBackup,GoogleAppIndexingWarning"> tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity <activity android:name="io.flutter.embedding.android.FlutterActivity"
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application> </application>
</manifest> </manifest>
package com.example.permissionhandlerexample;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment