diff --git a/README.md b/README.md
index c1dbd08d..67b5c8f0 100644
--- a/README.md
+++ b/README.md
@@ -129,6 +129,16 @@ Dexter.checkPermissions(new CompositePermissionListener(snackbarMultiplePermissi
**IMPORTANT**: Remember to follow the [Google design guidelines] [2] to make your application as user-friendly as possible.
+**If your application has to support configuration changes based on screen rotation remember to add a call to ``Dexter`` in your Activity ``onCreate`` method as follows:**
+
+```java
+ @Override protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.sample_activity);
+ Dexter.continuePendingRequestsIfPossible(permissionsListener);
+ }
+```
+
Add it to your project
----------------------
diff --git a/dexter/src/main/AndroidManifest.xml b/dexter/src/main/AndroidManifest.xml
index 30baf825..5ce10cb3 100644
--- a/dexter/src/main/AndroidManifest.xml
+++ b/dexter/src/main/AndroidManifest.xml
@@ -22,6 +22,7 @@
diff --git a/dexter/src/main/java/com/karumi/dexter/Dexter.java b/dexter/src/main/java/com/karumi/dexter/Dexter.java
index 5d45f7c4..8d7e8f58 100644
--- a/dexter/src/main/java/com/karumi/dexter/Dexter.java
+++ b/dexter/src/main/java/com/karumi/dexter/Dexter.java
@@ -92,10 +92,29 @@ public static boolean isRequestOngoing() {
}
/**
- * Method called whenever the DexterActivity has been created and is ready to be used
+ * Requests pending permissions if there were permissions lost. This method can be used to
+ * recover the Dexter state during a configuration change, for example when the device is
+ * rotated.
*/
- static void onActivityCreated(Activity activity) {
- instance.onActivityCreated(activity);
+ public static void continuePendingRequestsIfPossible(MultiplePermissionsListener listener) {
+ instance.continuePendingRequestsIfPossible(listener);
+ }
+
+ /**
+ * Requests pending permission if there was a permissions lost. This method can be used to
+ * recover the Dexter state during a configuration change, for example when the device is
+ * rotated.
+ */
+ public static void continuePendingRequestIfPossible(PermissionListener listener) {
+ instance.continuePendingRequestIfPossible(listener);
+ }
+
+ /**
+ * Method called whenever the DexterActivity has been created or recreated and is ready to be
+ * used.
+ */
+ static void onActivityReady(Activity activity) {
+ instance.onActivityReady(activity);
}
/**
diff --git a/dexter/src/main/java/com/karumi/dexter/DexterActivity.java b/dexter/src/main/java/com/karumi/dexter/DexterActivity.java
index c8288322..07bef390 100644
--- a/dexter/src/main/java/com/karumi/dexter/DexterActivity.java
+++ b/dexter/src/main/java/com/karumi/dexter/DexterActivity.java
@@ -17,6 +17,7 @@
package com.karumi.dexter;
import android.app.Activity;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.WindowManager;
@@ -27,10 +28,15 @@ public final class DexterActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Dexter.onActivityCreated(this);
+ Dexter.onActivityReady(this);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
}
+ @Override protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Dexter.onActivityReady(this);
+ }
+
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
Collection grantedPermissions = new LinkedList<>();
diff --git a/dexter/src/main/java/com/karumi/dexter/DexterInstance.java b/dexter/src/main/java/com/karumi/dexter/DexterInstance.java
index c957ac8d..d3b2881b 100644
--- a/dexter/src/main/java/com/karumi/dexter/DexterInstance.java
+++ b/dexter/src/main/java/com/karumi/dexter/DexterInstance.java
@@ -23,6 +23,7 @@
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest;
+import com.karumi.dexter.listener.multi.EmptyMultiplePermissionsListener;
import com.karumi.dexter.listener.multi.MultiplePermissionsListener;
import com.karumi.dexter.listener.single.PermissionListener;
import java.util.Collection;
@@ -38,6 +39,8 @@
final class DexterInstance {
private static final int PERMISSIONS_REQUEST_CODE = 42;
+ private static final MultiplePermissionsListener EMPTY_LISTENER =
+ new EmptyMultiplePermissionsListener();
private final Context context;
private final AndroidPermissionService androidPermissionService;
@@ -45,8 +48,9 @@ final class DexterInstance {
private final Collection pendingPermissions;
private final MultiplePermissionsReport multiplePermissionsReport;
private final AtomicBoolean isRequestingPermission;
+ private final AtomicBoolean rationaleAccepted;
private Activity activity;
- private MultiplePermissionsListener listener;
+ private MultiplePermissionsListener listener = EMPTY_LISTENER;
DexterInstance(Context context, AndroidPermissionService androidPermissionService,
IntentProvider intentProvider) {
@@ -56,6 +60,7 @@ final class DexterInstance {
this.pendingPermissions = new TreeSet<>();
this.multiplePermissionsReport = new MultiplePermissionsReport();
this.isRequestingPermission = new AtomicBoolean();
+ this.rationaleAccepted = new AtomicBoolean();
}
/**
@@ -86,15 +91,37 @@ void checkPermissions(MultiplePermissionsListener listener, Collection p
multiplePermissionsReport.clear();
this.listener = listener;
- startTransparentActivity();
+ startTransparentActivityIfNeeded();
}
/**
- * Method called whenever the inner activity has been created and is ready to be used
+ * Check if there is a permission pending to be confirmed by the user and restarts the
+ * request for permission process.
*/
- void onActivityCreated(Activity activity) {
- this.activity = activity;
+ void continuePendingRequestIfPossible(PermissionListener listener) {
+ MultiplePermissionsListenerToPermissionListenerAdapter adapter =
+ new MultiplePermissionsListenerToPermissionListenerAdapter(listener);
+ continuePendingRequestsIfPossible(adapter);
+ }
+
+ /**
+ * Check if there are some permissions pending to be confirmed by the user and restarts the
+ * request for permission process.
+ */
+ void continuePendingRequestsIfPossible(MultiplePermissionsListener listener) {
+ boolean isShowingRationale = !pendingPermissions.isEmpty() && !rationaleAccepted.get();
+ this.listener = listener;
+ if (isShowingRationale) {
+ onActivityReady(activity);
+ }
+ }
+ /**
+ * Method called whenever the inner activity has been created or restarted and is ready to be
+ * used.
+ */
+ void onActivityReady(Activity activity) {
+ this.activity = activity;
Collection deniedRequests = new LinkedList<>();
Collection grantedRequests = new LinkedList<>();
@@ -134,6 +161,7 @@ void onPermissionRequestDenied(Collection permissions) {
* with the permission request process
*/
void onContinuePermissionRequest() {
+ rationaleAccepted.set(true);
requestPermissionsToSystem(pendingPermissions);
}
@@ -142,6 +170,7 @@ void onContinuePermissionRequest() {
* the permission request process
*/
void onCancelPermissionRequest() {
+ rationaleAccepted.set(false);
updatePermissionsAsDenied(pendingPermissions);
}
@@ -162,7 +191,7 @@ void requestPermissionsToSystem(Collection permissions) {
permissions.toArray(new String[permissions.size()]), PERMISSIONS_REQUEST_CODE);
}
- private void startTransparentActivity() {
+ private void startTransparentActivityIfNeeded() {
Intent intent = intentProvider.get(context, DexterActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
@@ -183,7 +212,7 @@ private void handleDeniedPermissions(Collection permissions) {
if (shouldShowRequestRationalePermissions.isEmpty()) {
requestPermissionsToSystem(permissions);
- } else {
+ } else if (!rationaleAccepted.get()) {
PermissionRationaleToken permissionToken = new PermissionRationaleToken(this);
listener.onPermissionRationaleShouldBeShown(shouldShowRequestRationalePermissions,
permissionToken);
@@ -216,7 +245,9 @@ private void onPermissionsChecked(Collection permissions) {
if (pendingPermissions.isEmpty()) {
activity.finish();
isRequestingPermission.set(false);
+ rationaleAccepted.set(false);
listener.onPermissionsChecked(multiplePermissionsReport);
+ listener = EMPTY_LISTENER;
}
}
diff --git a/dexter/src/test/java/com/karumi/dexter/DexterInstanceTest.java b/dexter/src/test/java/com/karumi/dexter/DexterInstanceTest.java
index 1293324c..c1025e6f 100644
--- a/dexter/src/test/java/com/karumi/dexter/DexterInstanceTest.java
+++ b/dexter/src/test/java/com/karumi/dexter/DexterInstanceTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -104,6 +105,16 @@ public void onCheckPermissionMoreThanOnceThenThrowException() {
thenPermissionIsPermanentlyDenied(ANY_PERMISSION);
}
+ @Test public void onPermissionsPendingThenShouldNotShowPermissionRationaleTwice() {
+ givenPermissionIsAlreadyDenied(ANY_PERMISSION);
+ givenShouldShowRationaleForPermission(ANY_PERMISSION);
+
+ whenCheckPermission(permissionListener, ANY_PERMISSION);
+ whenContinueWithTheCheckPermissionProcess(permissionListener);
+
+ thenPermissionRationaleIsShown(2);
+ }
+
private void givenPermissionIsAlreadyDenied(String permission) {
givenPermissionIsChecked(permission, PackageManager.PERMISSION_DENIED);
}
@@ -129,7 +140,11 @@ private void givenShouldShowNotRationaleForPermission(String permission) {
private void whenCheckPermission(PermissionListener permissionListener, String permission) {
dexter.checkPermission(permissionListener, permission);
- dexter.onActivityCreated(activity);
+ dexter.onActivityReady(activity);
+ }
+
+ private void whenContinueWithTheCheckPermissionProcess(PermissionListener permissionListener) {
+ dexter.continuePendingRequestIfPossible(permissionListener);
}
private void thenPermissionIsGranted(String permission) {
@@ -153,6 +168,11 @@ private void thenShouldShowRationaleForPermission(String permission) {
isA(PermissionToken.class));
}
+ private void thenPermissionRationaleIsShown(int times) {
+ verify(permissionListener, times(times)).onPermissionRationaleShouldBeShown(
+ isA(PermissionRequest.class), isA(PermissionToken.class));
+ }
+
private static ArgumentMatcher getPermissionGrantedResponseMatcher(
final String permission) {
return new ArgumentMatcher() {
diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml
index 7d05c906..f39b4725 100644
--- a/sample/src/main/AndroidManifest.xml
+++ b/sample/src/main/AndroidManifest.xml
@@ -33,8 +33,6 @@
diff --git a/sample/src/main/java/com/karumi/dexter/sample/SampleActivity.java b/sample/src/main/java/com/karumi/dexter/sample/SampleActivity.java
index 8abd63a9..42220ff4 100644
--- a/sample/src/main/java/com/karumi/dexter/sample/SampleActivity.java
+++ b/sample/src/main/java/com/karumi/dexter/sample/SampleActivity.java
@@ -59,6 +59,12 @@ public class SampleActivity extends Activity {
setContentView(R.layout.sample_activity);
ButterKnife.bind(this);
createPermissionListeners();
+ /*
+ * If during the rotate screen process the activity has been restarted you can call this method
+ * to start with the check permission process without keep in an Android Bundle the state of
+ * the request permission process.
+ */
+ Dexter.continuePendingRequestsIfPossible(allPermissionsListener);
}
@OnClick(R.id.all_permissions_button) public void onAllPermissionsButtonClicked() {