/*
* Copyright (c) 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.samples.apps.iosched.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.view.View;
import com.google.samples.apps.iosched.BuildConfig;
import com.google.samples.apps.iosched.R;
import static com.google.samples.apps.iosched.util.LogUtils.LOGI;
import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag;
/**
* Set of runtime permission utility methods.
*/
public class PermissionsUtils {
private static final String TAG = makeLogTag(PermissionsUtils.class);
/**
* Determine if any permission is in the denied state.
*/
public static boolean anyPermissionDenied(Context context, String[] permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) ==
PackageManager.PERMISSION_DENIED) {
return true;
}
}
return false;
}
/**
* Displays a persistent {@link Snackbar} acknowledging the permission dismissal. If one of the
* permissions is detected in the "Don't ask again" state then the OK button forwards to the
* AppInfo screen, otherwise, the OK button invokes a permissions request.
* <p/>
* This method determines the Do-Not-Ask-Again-State by: 1.) Only being called from {@code
* onRequestPermissionResult} when at least one permission was not granted. 2.) Checking that a
* permission is currently in the permission-denied and no-rationale-needed states. 3.) The
* combination of 1 and 2 indicates that at least one permission is in the Do-Not-Ask state and
* the only resolution to that is for the user to visit the App Info -> Permissions screen.
*/
@NonNull
public static Snackbar displayConditionalPermissionDenialSnackbar(
@NonNull final Activity activity, final int messageResId, @NonNull String[] permissions,
int requestCode) {
return displayConditionalPermissionDenialSnackbar(activity, messageResId, permissions,
requestCode, true);
}
/**
* Displays a {@link Snackbar} acknowledging the permission dismissal. If one of the permissions
* is detected in the "Don't ask again" state then the OK button forwards to the AppInfo screen,
* otherwise, the OK button invokes a permissions request.
* <p/>
* This method determines the Do-Not-Ask-Again-State by: 1.) Only being called from {@code
* onRequestPermissionResult} when at least one permission was not granted. 2.) Checking that a
* permission is currently in the permission-denied and no-rationale-needed states. 3.) The
* combination of 1 and 2 indicates that at least one permission is in the Do-Not-Ask state and
* the only resolution to that is for the user to visit the App Info -> Permissions screen.
*/
@NonNull
public static Snackbar displayConditionalPermissionDenialSnackbar(
@NonNull final Activity activity, final int messageResId, @NonNull String[] permissions,
int requestCode, boolean isPersistent) {
boolean permissionInDoNotAskAgainState = false;
for (final String permission : permissions) {
if (ActivityCompat.checkSelfPermission(activity, permission) ==
PackageManager.PERMISSION_DENIED &&
!ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
permissionInDoNotAskAgainState = true;
break;
}
}
if (permissionInDoNotAskAgainState) {
// User has to manually enable permissions on the AppInfo screen.
return displayPermissionDeniedAppInfoResolutionSnackbar(activity, messageResId,
isPersistent);
} else {
// User clicks OK to re-start the permissions requests.
return displayPermissionRationaleSnackbar(activity, messageResId, permissions,
requestCode, isPersistent);
}
}
private static Snackbar displayPermissionDeniedAppInfoResolutionSnackbar(
@NonNull final Activity activity,
final int messageResId, final boolean isPersistent) {
View view = UIUtils.getRootView(activity);
final int length = isPersistent ? Snackbar.LENGTH_INDEFINITE : Snackbar.LENGTH_LONG;
Snackbar snackbar = Snackbar.make(view, messageResId, length)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(final View v) {
LOGI(TAG, "Invoking App Info screen");
final Intent intent =
new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
activity.startActivity(intent);
}
});
snackbar.show();
return snackbar;
}
/**
* A persistent Snackbar is displayed that gives a {@code permission} rationale and forwards to
* the App Info screen to allow the user to resolve the error.
*/
@NonNull
public static Snackbar displayPermissionDeniedAppInfoResolutionSnackbar(
@NonNull final Activity activity, final int messageResId) {
return displayPermissionDeniedAppInfoResolutionSnackbar(activity, messageResId, true);
}
/**
* A Snackbar is displayed that gives a {@code permission} rationale and an action that only
* dismisses the Snackbar.
*/
@NonNull
public static Snackbar displayPermissionDeniedSnackbar(@NonNull final Activity activity,
final int messageResId) {
View view = UIUtils.getRootView(activity);
final Snackbar snackbar = Snackbar.make(view, messageResId, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(final View v) {
snackbar.dismiss();
}
}).show();
return snackbar;
}
/**
* A persistent Snackbar is displayed that prompts for {@code permissions} again when OK is
* clicked.
*/
@NonNull
public static Snackbar displayPermissionRationaleSnackbar(@NonNull final Activity activity,
final int messageResId, @NonNull final String[] permissions, final int requestCode) {
return displayPermissionRationaleSnackbar(activity, messageResId, permissions, requestCode,
true);
}
/**
* A Snackbar is displayed that prompts for {@code permissions} again when OK is clicked.
*/
@NonNull
public static Snackbar displayPermissionRationaleSnackbar(@NonNull final Activity activity,
final int messageResId, @NonNull final String[] permissions, final int requestCode,
boolean isPersistent) {
View view = UIUtils.getRootView(activity);
final int length = isPersistent ? Snackbar.LENGTH_INDEFINITE : Snackbar.LENGTH_LONG;
Snackbar snackbar = Snackbar.make(view, messageResId, length)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(final View v) {
ActivityCompat.requestPermissions(activity, permissions,
requestCode);
}
});
snackbar.show();
return snackbar;
}
/**
* Determines if all {@code permissions} have already been granted.
*/
public static boolean permissionsAlreadyGranted(@NonNull Context context,
@NonNull String[] permissions) {
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) !=
PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* Determines if {@code permission} rationale should be displayed for any {@code permission}.
*/
public static boolean shouldShowAnyPermissionRationale(@NonNull Activity activity,
String[] permissions) {
for (String permission : permissions) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
return true;
}
}
return false;
}
}