What's The Best Way To Check For Permissions At Runtime Using Mvp Architecture?
Solution 1:
What I would do is:
The view will implement:
public Activity getViewActivity();
The presenter will implement:
publicvoidrequestPermissions();
publicvoidonPermissionsResult();
Inside requestPermissions
, the presenter will do: getViewActivity().checkSelfPermission; getViewActivity.requestPermissions(); etc.
The view will call inside the onRequestPermissionsResult
callback to presenter.onPermissionsResult();
With this all the logic will be implemented inside the presenter.
In my opinion, your presenter is decoupled: it won't depend on any view implementation (it will only depend on the view interface).
"I've heard that keeping your presenter free from Android code is good for testing." I don't understand this part. If the code is good, it can be tested without any problem.
Solution 2:
If you still want to be able to mock permission access/requests, you still create something like a PermissionHandler
, but only reference it inside your view class. For example -
Interface:
publicinterfacePermissionsHandler {
booleancheckHasPermission(AppCompatActivity activity, String permission);
voidrequestPermission(AppCompatActivity activity, String[] permissions, int requestCode);
}
Production implementation:
publicclassPermissionsHandlerAndroidimplementsPermissionsHandler {
@OverridepublicbooleancheckHasPermission(AppCompatActivity activity, String permission) {
returnContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED;
}
@OverridepublicvoidrequestPermission(AppCompatActivity activity, String[] permissions, int requestCode){
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
}
Mocked class (for example, to test and make sure your activity correctly handles onRequestPermissionsResult
)
publicclassPermissionsHandlerMockedimplementsPermissionsHandler {
@OverridepublicbooleancheckHasPermission(AppCompatActivity activity, String permission) {
returnfalse;
}
@OverridepublicvoidrequestPermission(AppCompatActivity activity, String[] permissions, int requestCode){
int[] grantResults = new int[permissions.length];
for (int i = 0; i < permissions.length; i++) {
grantResults[i] = PackageManager.PERMISSION_GRANTED
}
activity.onRequestPermissionResult(requestCode, permissions, grantResults);
}
}
Then in your activity:
PermissionsHandler permissionsHandler;
@OverrideprotectedvoidonCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
permissionsHandler = Injection.providePermissionsHandler();
//or however you choose to inject your production vs mocked handler.
}
//method from your view interface, to be called by your presenter@OverridevoidrequestLocationPermission() {
permissionsHandler.requestPermision((AppCompatActivity) this, newString[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE_LOCATION};
}
fobo66, you can always make the view implement much more generic methods like
checkLocationPermissionGranted()
and requestLocationPermission()
. Then your view implementation can reference the activity as needed, and your presenter never has to touch the activity reference.
Solution 3:
Permissions requests and status are View(Fragment or Activity) responsibility due to depend of the user actions to make a request or grant permissions. I manage permissions with MVP as follows(Read external storage example):
My Contract
interfaceView {
...
voidrequestReadPermission();
boolean areReadPermissionGranted();
voidshowPermissionError();
voidhidePermissionError();
...
}
interfacePresenter {
...
voidsetReadPermissions(boolean grantedPermissions);
...
}
interfaceModel {
...
}
My view implementation. (Fragment in this case but it can be Activity or whatever, the Presenter will only expect the response).
publicclassMyViewextendsFragmentimplementsContract.View {
...
Contract.Presenter presenter;
@OverridepublicvoidonRequestPermissionsResult(int requestCode, @NonNullString[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
boolean grantedPermissions = (grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED);
presenter.setReadPermissions(grantedPermissions);
}
@OverridepublicvoidshowPermissionError() {
// Show not permission message
}
@OverridepublicvoidhidePermissionError() {
// Hide not permission message
}
@OverridepublicvoidrequestReadPermission() {
this.requestPermissions(newString[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
@OverridepublicbooleanareReadPermissionGranted() {
returnContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
...
And the Presenter implementation
publicclassMyPresenterimplementsContract.Presenter {
...
Contract.View view;
publicvoiddoSomethingThatRequiresPermissions() {
...
if ( !view.areReadPermissionGranted() ) {
view.requestReadPermission();
view.showPermissionError();
} else {
view.hidePermissionError();
doSomethingWithPermissionsGranted();
}
...
}
@OverridepublicvoidsetReadPermissions(boolean grantedPermissions) {
if( grantedPermissions ){
view.hidePermissionError();
doSomethingThatRequiresPermissions();
} else {
view.showPermissionError();
}
}
publicvoiddoSomethingWithPermissionsGranted(){
...
}
Then you can make unit test like
Contract.View mockedView;
@Test
public void requestAlbumListWithoutPermissions() {
when(mockedView.areReadPermissionGranted()).thenReturn(false);
presenter.doSomethingWithPermissionsGranted();
verify(mockedView).showPermissionError();
verify(mockedView).requestReadPermission();
}
Post a Comment for "What's The Best Way To Check For Permissions At Runtime Using Mvp Architecture?"