Skip to content
This repository was archived by the owner on Oct 3, 2024. It is now read-only.

Commit a5cb77f

Browse files
koshsamtstern
authored andcommitted
Retaining rationale dialogs and preventing dialog cancellation. (#52)
1 parent 74cad92 commit a5cb77f

File tree

4 files changed

+341
-89
lines changed

4 files changed

+341
-89
lines changed

easypermissions/src/main/java/pub/devrel/easypermissions/EasyPermissions.java

Lines changed: 126 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,20 @@
1818
import android.annotation.TargetApi;
1919
import android.app.Activity;
2020
import android.content.Context;
21-
import android.content.DialogInterface;
22-
import android.content.Intent;
2321
import android.content.pm.PackageManager;
24-
import android.net.Uri;
2522
import android.os.Build;
26-
import android.provider.Settings;
23+
import android.support.annotation.NonNull;
2724
import android.support.annotation.Nullable;
2825
import android.support.annotation.StringRes;
2926
import android.support.v4.app.ActivityCompat;
3027
import android.support.v4.app.Fragment;
3128
import android.support.v4.content.ContextCompat;
32-
import android.support.v7.app.AlertDialog;
29+
import android.support.v7.app.AppCompatActivity;
3330
import android.util.Log;
3431

3532
import java.lang.reflect.InvocationTargetException;
3633
import java.lang.reflect.Method;
3734
import java.util.ArrayList;
38-
import java.util.Arrays;
3935
import java.util.List;
4036

4137
/**
@@ -44,6 +40,8 @@
4440
public class EasyPermissions {
4541

4642
private static final String TAG = "EasyPermissions";
43+
private static final String DIALOG_TAG = "RationaleDialogFragment";
44+
4745

4846
public interface PermissionCallbacks extends
4947
ActivityCompat.OnRequestPermissionsResultCallback {
@@ -57,12 +55,13 @@ public interface PermissionCallbacks extends
5755
/**
5856
* Check if the calling context has a set of permissions.
5957
*
60-
* @param context the calling context.
61-
* @param perms one ore more permissions, such as {@code android.Manifest.permission.CAMERA}.
62-
* @return true if all permissions are already granted, false if at least one permission
63-
* is not yet granted.
58+
* @param context
59+
* the calling context.
60+
* @param perms
61+
* one ore more permissions, such as {@code android.Manifest.permission.CAMERA}.
62+
* @return true if all permissions are already granted, false if at least one permission is not yet granted.
6463
*/
65-
public static boolean hasPermissions(Context context, String... perms) {
64+
public static boolean hasPermissions(@NonNull Context context, @NonNull String... perms) {
6665
// Always return true for SDK < M, let the system deal with the permissions
6766
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
6867
Log.w(TAG, "hasPermissions: API version < M, returning true by default");
@@ -83,17 +82,20 @@ public static boolean hasPermissions(Context context, String... perms) {
8382
/**
8483
* Request a set of permissions, showing rationale if the system requests it.
8584
*
86-
* @param object Activity or Fragment requesting permissions. Should implement
87-
* {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
88-
* or
89-
* {@link android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback}
90-
* @param rationale a message explaining why the application needs this set of permissions, will
91-
* be displayed if the user rejects the request the first time.
92-
* @param requestCode request code to track this request, must be < 256.
93-
* @param perms a set of permissions to be requested.
85+
* @param object
86+
* Activity or Fragment requesting permissions. Should implement
87+
* {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
88+
* or {@link android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback}
89+
* @param rationale
90+
* a message explaining why the application needs this set of permissions, will be displayed if the user rejects the request the first
91+
* time.
92+
* @param requestCode
93+
* request code to track this request, must be < 256.
94+
* @param perms
95+
* a set of permissions to be requested.
9496
*/
95-
public static void requestPermissions(final Object object, String rationale,
96-
final int requestCode, final String... perms) {
97+
public static void requestPermissions(@NonNull final Object object, @NonNull String rationale,
98+
final int requestCode, @NonNull final String... perms) {
9799
requestPermissions(object, rationale,
98100
android.R.string.ok,
99101
android.R.string.cancel,
@@ -103,21 +105,25 @@ public static void requestPermissions(final Object object, String rationale,
103105
/**
104106
* Request a set of permissions, showing rationale if the system requests it.
105107
*
106-
* @param object Activity or Fragment requesting permissions. Should implement
107-
* {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
108-
* or
109-
* {@link android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback}
110-
* @param rationale a message explaining why the application needs this set of permissions, will
111-
* be displayed if the user rejects the request the first time.
112-
* @param positiveButton custom text for positive button
113-
* @param negativeButton custom text for negative button
114-
* @param requestCode request code to track this request, must be < 256.
115-
* @param perms a set of permissions to be requested.
108+
* @param object
109+
* Activity or Fragment requesting permissions. Should implement
110+
* {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback}
111+
* or {@link android.support.v13.app.FragmentCompat.OnRequestPermissionsResultCallback}
112+
* @param rationale
113+
* a message explaining why the application needs this set of permissions, will be displayed if the user rejects the request the first
114+
* time.
115+
* @param positiveButton
116+
* custom text for positive button
117+
* @param negativeButton
118+
* custom text for negative button
119+
* @param requestCode
120+
* request code to track this request, must be < 256.
121+
* @param perms
122+
* a set of permissions to be requested.
116123
*/
117-
public static void requestPermissions(final Object object, String rationale,
118-
@StringRes int positiveButton,
119-
@StringRes int negativeButton,
120-
final int requestCode, final String... perms) {
124+
public static void requestPermissions(@NonNull final Object object, @NonNull String rationale,
125+
@StringRes int positiveButton, @StringRes int negativeButton,
126+
final int requestCode, @NonNull final String... perms) {
121127

122128
checkCallingObjectSuitability(object);
123129

@@ -128,45 +134,69 @@ public static void requestPermissions(final Object object, String rationale,
128134
}
129135

130136
if (shouldShowRationale) {
131-
Activity activity = getActivity(object);
132-
if (null == activity) {
133-
return;
137+
if (object instanceof AppCompatActivity) {
138+
showAppCompatRationaleDialog((AppCompatActivity) object, requestCode,
139+
perms, positiveButton, negativeButton, rationale);
140+
} else if (object instanceof Activity) {
141+
showRationaleDialog((Activity) object, requestCode, perms,
142+
positiveButton, negativeButton, rationale);
143+
} else if (object instanceof Fragment) {
144+
showAppCompatRationaleDialogByFragment((Fragment) object, requestCode, perms,
145+
positiveButton, negativeButton, rationale);
146+
} else if (object instanceof android.app.Fragment) {
147+
showRationaleDialogByFragment((android.app.Fragment) object, requestCode, perms,
148+
positiveButton, negativeButton, rationale);
149+
} else {
150+
throw new RuntimeException("Object is not assigned to either an Activity or Fragment.");
134151
}
135-
136-
AlertDialog dialog = new AlertDialog.Builder(activity)
137-
.setMessage(rationale)
138-
.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() {
139-
@Override
140-
public void onClick(DialogInterface dialog, int which) {
141-
executePermissionsRequest(object, perms, requestCode);
142-
}
143-
})
144-
.setNegativeButton(negativeButton, new DialogInterface.OnClickListener() {
145-
@Override
146-
public void onClick(DialogInterface dialog, int which) {
147-
// act as if the permissions were denied
148-
if (object instanceof PermissionCallbacks) {
149-
((PermissionCallbacks) object).onPermissionsDenied(requestCode, Arrays.asList(perms));
150-
}
151-
}
152-
}).create();
153-
dialog.show();
154152
} else {
155153
executePermissionsRequest(object, perms, requestCode);
156154
}
157155
}
158156

159157

158+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
159+
private static void showRationaleDialogByFragment(@NonNull android.app.Fragment object, int requestCode,
160+
@NonNull String[] perms, @StringRes int positiveButton,
161+
@StringRes int negativeButton, @NonNull String rationale) {
162+
RationaleDialogFragment.newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
163+
.show(object.getChildFragmentManager(), DIALOG_TAG);
164+
}
165+
166+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
167+
private static void showRationaleDialog(@NonNull Activity object, int requestCode,
168+
@NonNull String[] perms, @StringRes int positiveButton,
169+
@StringRes int negativeButton, @NonNull String rationale) {
170+
RationaleDialogFragment.newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
171+
.show(object.getFragmentManager(), DIALOG_TAG);
172+
}
173+
174+
private static void showAppCompatRationaleDialogByFragment(@NonNull Fragment object, int requestCode,
175+
@NonNull String[] perms, @StringRes int positiveButton,
176+
@StringRes int negativeButton, @NonNull String rationale) {
177+
RationaleAppCompatDialogFragment.newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
178+
.show(object.getChildFragmentManager(), DIALOG_TAG);
179+
}
180+
181+
private static void showAppCompatRationaleDialog(@NonNull AppCompatActivity object, int requestCode,
182+
@NonNull String[] perms, @StringRes int positiveButton,
183+
@StringRes int negativeButton, @NonNull String rationale) {
184+
RationaleAppCompatDialogFragment.newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
185+
.show(object.getSupportFragmentManager(), DIALOG_TAG);
186+
}
187+
188+
160189
/**
161-
* Check if at least one permission in the list of denied permissions has been permanently
162-
* denied (user clicked "Never ask again").
163-
* @param object Activity or Fragment requesting permissions.
164-
* @param deniedPermissions list of denied permissions, usually from
165-
* {@link PermissionCallbacks#onPermissionsDenied(int, List)}
190+
* Check if at least one permission in the list of denied permissions has been permanently denied (user clicked "Never ask again").
191+
*
192+
* @param object
193+
* Activity or Fragment requesting permissions.
194+
* @param deniedPermissions
195+
* list of denied permissions, usually from {@link PermissionCallbacks#onPermissionsDenied(int, List)}
166196
* @return {@code true} if at least one permission in the list was permanently denied.
167197
*/
168-
public static boolean somePermissionPermanentlyDenied(Object object,
169-
List<String> deniedPermissions) {
198+
public static boolean somePermissionPermanentlyDenied(@NonNull Object object,
199+
@NonNull List<String> deniedPermissions) {
170200
for (String deniedPermission : deniedPermissions) {
171201
if (permissionPermanentlyDenied(object, deniedPermission)) {
172202
return true;
@@ -179,32 +209,38 @@ public static boolean somePermissionPermanentlyDenied(Object object,
179209

180210
/**
181211
* Check if a permission has been permanently denied (user clicked "Never ask again").
182-
* @param object Activity or Fragment requesting permissions.
183-
* @param deniedPermission denied permission.
212+
*
213+
* @param object
214+
* Activity or Fragment requesting permissions.
215+
* @param deniedPermission
216+
* denied permission.
184217
* @return {@code true} if the permissions has been permanently denied.
185218
*/
186-
public static boolean permissionPermanentlyDenied(Object object, String deniedPermission) {
219+
public static boolean permissionPermanentlyDenied(@NonNull Object object,
220+
@NonNull String deniedPermission) {
187221
return !shouldShowRequestPermissionRationale(object, deniedPermission);
188222
}
189223

190224

191225
/**
192-
* Handle the result of a permission request, should be called from the calling Activity's
193-
* {@link android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])}
194-
* method.
226+
* Handle the result of a permission request, should be called from the calling Activity's {@link android.support.v4.app.ActivityCompat
227+
* .OnRequestPermissionsResultCallback#onRequestPermissionsResult(int, String[], int[])} method.
195228
* <p>
196-
* If any permissions were granted or denied, the {@code object} will receive the appropriate
197-
* callbacks through {@link PermissionCallbacks} and methods annotated with
198-
* {@link AfterPermissionGranted} will be run if appropriate.
229+
* If any permissions were granted or denied, the {@code object} will receive the appropriate callbacks through {@link PermissionCallbacks} and
230+
* methods annotated with {@link AfterPermissionGranted} will be run if appropriate.
199231
*
200-
* @param requestCode requestCode argument to permission result callback.
201-
* @param permissions permissions argument to permission result callback.
202-
* @param grantResults grantResults argument to permission result callback.
203-
* @param receivers an array of objects that have a method annotated with {@link AfterPermissionGranted}
204-
* or implement {@link PermissionCallbacks}.
232+
* @param requestCode
233+
* requestCode argument to permission result callback.
234+
* @param permissions
235+
* permissions argument to permission result callback.
236+
* @param grantResults
237+
* grantResults argument to permission result callback.
238+
* @param receivers
239+
* an array of objects that have a method annotated with {@link AfterPermissionGranted} or implement {@link PermissionCallbacks}.
205240
*/
206-
public static void onRequestPermissionsResult(int requestCode, String[] permissions,
207-
int[] grantResults, Object... receivers) {
241+
public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
242+
@NonNull int[] grantResults,
243+
@NonNull Object... receivers) {
208244

209245
// Make a collection of granted and denied permissions from the request.
210246
ArrayList<String> granted = new ArrayList<>();
@@ -243,7 +279,8 @@ public static void onRequestPermissionsResult(int requestCode, String[] permissi
243279
}
244280

245281
@TargetApi(23)
246-
private static boolean shouldShowRequestPermissionRationale(Object object, String perm) {
282+
private static boolean shouldShowRequestPermissionRationale(@NonNull Object object,
283+
@NonNull String perm) {
247284
if (object instanceof Activity) {
248285
return ActivityCompat.shouldShowRequestPermissionRationale((Activity) object, perm);
249286
} else if (object instanceof Fragment) {
@@ -256,9 +293,8 @@ private static boolean shouldShowRequestPermissionRationale(Object object, Strin
256293
}
257294

258295
@TargetApi(23)
259-
private static void executePermissionsRequest(Object object, String[] perms, int requestCode) {
296+
static void executePermissionsRequest(@NonNull Object object, @NonNull String[] perms, int requestCode) {
260297
checkCallingObjectSuitability(object);
261-
262298
if (object instanceof Activity) {
263299
ActivityCompat.requestPermissions((Activity) object, perms, requestCode);
264300
} else if (object instanceof Fragment) {
@@ -269,7 +305,7 @@ private static void executePermissionsRequest(Object object, String[] perms, int
269305
}
270306

271307
@TargetApi(11)
272-
private static Activity getActivity(Object object) {
308+
private static Activity getActivity(@NonNull Object object) {
273309
if (object instanceof Activity) {
274310
return ((Activity) object);
275311
} else if (object instanceof Fragment) {
@@ -281,7 +317,7 @@ private static Activity getActivity(Object object) {
281317
}
282318
}
283319

284-
private static void runAnnotatedMethods(Object object, int requestCode) {
320+
private static void runAnnotatedMethods(@NonNull Object object, int requestCode) {
285321
Class clazz = object.getClass();
286322
if (isUsingAndroidAnnotations(object)) {
287323
clazz = clazz.getSuperclass();
@@ -313,13 +349,15 @@ private static void runAnnotatedMethods(Object object, int requestCode) {
313349
}
314350
}
315351

316-
private static void checkCallingObjectSuitability(Object object) {
352+
private static void checkCallingObjectSuitability(@Nullable Object object) {
353+
if (object == null) {
354+
throw new NullPointerException("Activity or Fragment should not be null");
355+
}
317356
// Make sure Object is an Activity or Fragment
318357
boolean isActivity = object instanceof Activity;
319358
boolean isSupportFragment = object instanceof Fragment;
320359
boolean isAppFragment = object instanceof android.app.Fragment;
321360
boolean isMinSdkM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
322-
323361
if (!(isSupportFragment || isActivity || (isAppFragment && isMinSdkM))) {
324362
if (isAppFragment) {
325363
throw new IllegalArgumentException(
@@ -330,11 +368,10 @@ private static void checkCallingObjectSuitability(Object object) {
330368
}
331369
}
332370

333-
private static boolean isUsingAndroidAnnotations(Object object) {
371+
private static boolean isUsingAndroidAnnotations(@NonNull Object object) {
334372
if (!object.getClass().getSimpleName().endsWith("_")) {
335373
return false;
336374
}
337-
338375
try {
339376
Class clazz = Class.forName("org.androidannotations.api.view.HasViews");
340377
return clazz.isInstance(object);

0 commit comments

Comments
 (0)