Commit 4a48af08 by Maurits van Beusekom

Removed the need for ServiceStatus.unknown

parent 17e196ad
......@@ -5,26 +5,35 @@ 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;
@FunctionalInterface
interface OpenAppSettingsSuccessCallback {
void onSuccess(boolean appSettingsOpenedSuccessfully);
}
void openAppSettings(
Context context,
OpenAppSettingsSuccessCallback successCallback,
ErrorCallback errorCallback) {
if(context == null) {
Log.d(PermissionConstants.LOG_TAG, "Context cannot be null.");
errorCallback.onError("PermissionHandler.AppSettingsManager", "Android context cannot be null.");
return;
}
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.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);
applicationContext.startActivity(settingsIntent);
context.startActivity(settingsIntent);
return true;
successCallback.onSuccess(true);
} catch (Exception ex) {
return false;
successCallback.onSuccess(false);
}
}
}
package com.baseflow.permissionhandler;
@FunctionalInterface
interface ErrorCallback {
void onError(String errorCode, String errorDescription);
}
......@@ -51,24 +51,30 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
public void onMethodCall(@NonNull MethodCall call, @NonNull final Result result)
{
switch (call.method) {
case "checkPermissionStatus": {
case "checkServiceStatus": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
@PermissionConstants.PermissionStatus final int permissionStatus =
permissionManager.checkPermissionStatus(
permission,
activity);
serviceManager.checkServiceStatus(
permission,
applicationContext,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
result.success(permissionStatus);
break;
}
case "checkServiceStatus": {
case "checkPermissionStatus": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
@PermissionConstants.ServiceStatus final int serviceStatus =
serviceManager.checkServiceStatus(
permission,
activity);
permissionManager.checkPermissionStatus(
permission,
activity,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
result.success(serviceStatus);
break;
}
case "requestPermissions":
......@@ -79,21 +85,34 @@ final class MethodCallHandlerImpl implements MethodChannel.MethodCallHandler {
activityRegistry,
permissionRegistry,
result::success,
(String errorCode, String errorDescription) -> {
result.error(errorCode, errorDescription, null);
});
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
case "shouldShowRequestPermissionRationale": {
@PermissionConstants.PermissionGroup final int permission = Integer.parseInt(call.arguments.toString());
final boolean showRationale = permissionManager
.shouldShowRequestPermissionRationale(permission, activity);
result.success(showRationale);
permissionManager.shouldShowRequestPermissionRationale(
permission,
activity,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
}
case "openAppSettings":
boolean isOpen = appSettingsManager.openAppSettings(applicationContext);
result.success(isOpen);
appSettingsManager.openAppSettings(
applicationContext,
result::success,
(String errorCode, String errorDescription) -> result.error(
errorCode,
errorDescription,
null));
break;
default:
result.notImplemented();
......
......@@ -80,14 +80,12 @@ final class PermissionConstants {
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,
SERVICE_STATUS_NOT_APPLICABLE
})
@interface ServiceStatus {
}
......
......@@ -36,16 +36,16 @@ public final class PermissionHandlerPlugin implements FlutterPlugin, ActivityAwa
* <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) {
final PermissionHandlerPlugin permissionHandlerPlugin = new PermissionHandlerPlugin();
permissionHandlerPlugin.startListening(
registrar.context(),
registrar.activity(),
registrar.messenger(),
registrar::addActivityResultListener,
registrar::addRequestPermissionsResultListener
);
}
public static void registerWith(Registrar registrar) {
final PermissionHandlerPlugin permissionHandlerPlugin = new PermissionHandlerPlugin();
permissionHandlerPlugin.startListening(
registrar.context(),
registrar.activity(),
registrar.messenger(),
registrar::addActivityResultListener,
registrar::addRequestPermissionsResultListener
);
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
......
......@@ -23,83 +23,50 @@ import java.util.Map;
import io.flutter.plugin.common.PluginRegistry;
final class PermissionManager {
@FunctionalInterface
interface ActivityRegistry {
void addListener(PluginRegistry.ActivityResultListener handler);
}
@FunctionalInterface
interface PermissionRegistry {
void addListener(PluginRegistry.RequestPermissionsResultListener handler);
}
interface ResultCallback {
void onResult(Map<Integer, Integer> results);
@FunctionalInterface
interface RequestPermissionsSuccessCallback {
void onSuccess(Map<Integer, Integer> results);
}
interface ErrorCallback {
void onError(String errorCode, String errorDescription);
@FunctionalInterface
interface CheckPermissionsSuccessCallback {
void onSuccess(@PermissionConstants.PermissionStatus int permissionStatus);
}
@FunctionalInterface
interface ShouldShowRequestPermissionRationaleSuccessCallback {
void onSuccess(boolean shouldShowRequestPermissionRationale);
}
private boolean ongoing = false;
@PermissionConstants.PermissionStatus
int checkPermissionStatus(
void 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;
Activity activity,
CheckPermissionsSuccessCallback successCallback,
ErrorCallback errorCallback) {
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;
}
}
if(activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Activity cannot be null.");
errorCallback.onError(
"PermissionHandler.PermissionManager",
"Android activity is required to check for permissions and cannot be null.");
return;
}
return PermissionConstants.PERMISSION_STATUS_GRANTED;
successCallback.onSuccess(determinePermissionStatus(
permission,
activity));
}
void requestPermissions(
......@@ -107,25 +74,28 @@ final class PermissionManager {
Activity activity,
ActivityRegistry activityRegistry,
PermissionRegistry permissionRegistry,
ResultCallback resultCallback,
RequestPermissionsSuccessCallback successCallback,
ErrorCallback errorCallback) {
if(ongoing) {
errorCallback.onError(
"ERROR_ALREADY_REQUESTING_PERMISSIONS",
"PermissionHandler.PermissionManager",
"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).");
return;
}
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.");
errorCallback.onError(
"PermissionHandler.PermissionManager",
"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);
@PermissionConstants.PermissionStatus final int permissionStatus = determinePermissionStatus(permission, activity);
if (permissionStatus == PermissionConstants.PERMISSION_STATUS_GRANTED) {
if (!requestResults.containsKey(permission)) {
requestResults.put(permission, PermissionConstants.PERMISSION_STATUS_GRANTED);
......@@ -147,7 +117,7 @@ final class PermissionManager {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
activityRegistry.addListener(
new ActivityResultListener(resultCallback)
new ActivityResultListener(successCallback)
);
String packageName = activity.getPackageName();
......@@ -168,7 +138,7 @@ final class PermissionManager {
requestResults,
(Map<Integer, Integer> results) -> {
ongoing = false;
resultCallback.onResult(results);
successCallback.onSuccess(results);
})
);
......@@ -181,15 +151,82 @@ final class PermissionManager {
} else {
ongoing = false;
if (requestResults.size() > 0) {
resultCallback.onResult(requestResults);
successCallback.onSuccess(requestResults);
}
}
}
@PermissionConstants.PermissionStatus
private int determinePermissionStatus(
@PermissionConstants.PermissionGroup int permission,
Activity activity) {
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;
}
boolean shouldShowRequestPermissionRationale(int permission, Activity activity) {
void shouldShowRequestPermissionRationale(
int permission,
Activity activity,
ShouldShowRequestPermissionRationaleSuccessCallback successCallback,
ErrorCallback errorCallback) {
if (activity == null) {
Log.d(PermissionConstants.LOG_TAG, "Unable to detect current Activity.");
return false;
errorCallback.onError(
"PermissionHandler.PermissionManager",
"Unable to detect current Android Activity.");
return;
}
List<String> names = PermissionUtils.getManifestNames(activity, permission);
......@@ -197,15 +234,17 @@ final class PermissionManager {
// 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;
successCallback.onSuccess(false);
return;
}
if (names.isEmpty()) {
Log.d(PermissionConstants.LOG_TAG, "No permissions found in manifest for: " + permission + " no need to show request rationale");
return false;
successCallback.onSuccess(false);
return;
}
return ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0));
successCallback.onSuccess(ActivityCompat.shouldShowRequestPermissionRationale(activity, names.get(0)));
}
private int checkNotificationPermissionStatus(Context context) {
......@@ -227,10 +266,10 @@ final class PermissionManager {
// call.
boolean alreadyCalled = false;
final ResultCallback callback;
final RequestPermissionsSuccessCallback callback;
@VisibleForTesting
ActivityResultListener(ResultCallback callback) {
ActivityResultListener(RequestPermissionsSuccessCallback callback) {
this.callback = callback;
}
......@@ -245,7 +284,7 @@ final class PermissionManager {
? PermissionConstants.PERMISSION_STATUS_GRANTED
: PermissionConstants.PERMISSION_STATUS_DENIED;
callback.onResult(new HashMap<>(PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS, status));
callback.onSuccess(new HashMap<>(PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS, status));
return true;
}
}
......@@ -261,14 +300,14 @@ final class PermissionManager {
boolean alreadyCalled = false;
final Activity activity;
final ResultCallback callback;
final RequestPermissionsSuccessCallback callback;
final Map<Integer, Integer> requestResults;
@VisibleForTesting
RequestPermissionsListener(
Activity activity,
Map<Integer, Integer> requestResults,
ResultCallback callback) {
RequestPermissionsSuccessCallback callback) {
this.activity = activity;
this.callback = callback;
this.requestResults = requestResults;
......@@ -338,7 +377,7 @@ final class PermissionManager {
PermissionUtils.updatePermissionShouldShowStatus(this.activity, permission);
}
this.callback.onResult(requestResults);
this.callback.onSuccess(requestResults);
return true;
}
}
......
package com.baseflow.permissionhandler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
......@@ -18,34 +17,45 @@ import android.util.Log;
import java.util.List;
final class ServiceManager {
@PermissionConstants.ServiceStatus
int checkServiceStatus(
@FunctionalInterface
interface SuccessCallback {
void onSuccess(@PermissionConstants.ServiceStatus int serviceStatus);
}
void 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;
Context context,
SuccessCallback successCallback,
ErrorCallback errorCallback) {
if(context == null) {
Log.d(PermissionConstants.LOG_TAG, "Context cannot be null.");
errorCallback.onError("PermissionHandler.ServiceManager", "Android context cannot be null.");
return;
}
if (permission == PermissionConstants.PERMISSION_GROUP_LOCATION ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_ALWAYS ||
permission == PermissionConstants.PERMISSION_GROUP_LOCATION_WHEN_IN_USE) {
return isLocationServiceEnabled(activity)
final int serviceStatus = isLocationServiceEnabled(context)
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_DISABLED;
successCallback.onSuccess(serviceStatus);
}
if (permission == PermissionConstants.PERMISSION_GROUP_PHONE) {
PackageManager pm = activity.getPackageManager();
PackageManager pm = context.getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
TelephonyManager telephonyManager = (TelephonyManager) activity
TelephonyManager telephonyManager = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager == null || telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
Intent callIntent = new Intent(Intent.ACTION_CALL);
......@@ -53,23 +63,28 @@ final class ServiceManager {
List<ResolveInfo> callAppsList = pm.queryIntentActivities(callIntent, 0);
if (callAppsList.isEmpty()) {
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
return;
}
if (telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY) {
return PermissionConstants.SERVICE_STATUS_DISABLED;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_DISABLED);
return;
}
return PermissionConstants.SERVICE_STATUS_ENABLED;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_ENABLED);
return;
}
if (permission == PermissionConstants.PERMISSION_GROUP_IGNORE_BATTERY_OPTIMIZATIONS) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
final int serviceStatus = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
? PermissionConstants.SERVICE_STATUS_ENABLED
: PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(serviceStatus);
return;
}
return PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE;
successCallback.onSuccess(PermissionConstants.SERVICE_STATUS_NOT_APPLICABLE);
}
private boolean isLocationServiceEnabled(Context context) {
......@@ -87,7 +102,7 @@ final class ServiceManager {
}
}
// Suppress deprecation warnings since it's purpose is to support to be backwards compatible with
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre Pie versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnablePrePie(Context context)
......@@ -109,7 +124,7 @@ final class ServiceManager {
return locationMode != Settings.Secure.LOCATION_MODE_OFF;
}
// Suppress deprecation warnings since it's purpose is to support to be backwards compatible with
// Suppress deprecation warnings since its purpose is to support to be backwards compatible with
// pre KitKat versions of Android.
@SuppressWarnings("deprecation")
private static boolean isLocationServiceEnablePreKitKat(Context context)
......
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