From 688115874338edfd0a4aeb7706ccddccc52272a7 Mon Sep 17 00:00:00 2001 From: Srishti Rohatgi <53987325+srishti-R@users.noreply.github.com> Date: Fri, 22 Sep 2023 06:09:28 +0530 Subject: [PATCH] Android 13 permission related fixes (#5299) * Android 13 permission related fixes * removes audio and video as permissions --- app/src/main/AndroidManifest.xml | 2 + .../contributions/ContributionController.java | 4 +- .../contributions/ContributionsFragment.java | 4 +- .../commons/contributions/MainActivity.java | 2 +- .../explore/map/ExploreMapFragment.java | 6 +- .../location/LocationPermissionsHelper.java | 2 +- .../commons/media/MediaDetailFragment.java | 13 +- .../fragments/NearbyParentFragment.java | 6 +- .../commons/settings/SettingsFragment.java | 40 +++-- .../nrw/commons/upload/UploadActivity.java | 58 +++---- .../free/nrw/commons/utils/DownloadUtils.kt | 2 +- .../nrw/commons/utils/PermissionUtils.java | 148 ++++++++++-------- 12 files changed, 162 insertions(+), 125 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6e84bf23f..f564bb661 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,8 @@ + + diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 755766fd4..f23a148ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -56,7 +56,7 @@ public class ContributionController { } PermissionUtils.checkPermissionsAndPerformAction(activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE, + PermissionUtils.PERMISSIONS_STORAGE, () -> { if (defaultKvStore.getBoolean("inAppCameraFirstRun")) { defaultKvStore.putBoolean("inAppCameraFirstRun", false); @@ -159,7 +159,7 @@ public class ContributionController { setPickerConfiguration(activity,true); PermissionUtils.checkPermissionsAndPerformAction(activity, - Manifest.permission.WRITE_EXTERNAL_STORAGE, + PermissionUtils.PERMISSIONS_STORAGE, () -> FilePicker.openCustomSelector(activity, 0), R.string.storage_permission_title, R.string.write_storage_permission_rationale); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 249884b4b..80c709f3a 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -439,7 +439,7 @@ public class ContributionsFragment } private void checkPermissionsAndShowNearbyCardView() { - if (PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) { + if (PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { onLocationPermissionGranted(); } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) && store.getBoolean("displayLocationPermissionForCardView", true) @@ -452,7 +452,7 @@ public class ContributionsFragment private void requestLocationPermission() { PermissionUtils.checkPermissionsAndPerformAction(getActivity(), - Manifest.permission.ACCESS_FINE_LOCATION, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, this::onLocationPermissionGranted, this::displayYouWontSeeNearbyMessage, -1, diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 1861f5482..6a14f8ec6 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -165,7 +165,7 @@ public class MainActivity extends BaseActivity if (VERSION.SDK_INT >= VERSION_CODES.Q) { PermissionUtils.checkPermissionsAndPerformAction( this, - permission.ACCESS_MEDIA_LOCATION, + new String[]{permission.ACCESS_MEDIA_LOCATION}, () -> {}, R.string.media_location_permission_denied, R.string.add_location_manually diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java index d2e6a432b..a916c20cb 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java @@ -263,7 +263,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment private void performMapReadyActions() { if (isMapBoxReady) { if(!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || - PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)){ + PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})){ checkPermissionsAndPerformAction(); }else{ isPermissionDenied = true; @@ -404,8 +404,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment public void checkPermissionsAndPerformAction() { Timber.d("Checking permission and perfoming action"); PermissionUtils.checkPermissionsAndPerformAction(getActivity(), - Manifest.permission.ACCESS_FINE_LOCATION, - () -> locationPermissionGranted(), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + this::locationPermissionGranted, () -> isPermissionDenied = true, R.string.location_permission_title, R.string.location_permission_rationale_nearby); diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java index d618fa62e..a2055c286 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.java @@ -56,7 +56,7 @@ public class LocationPermissionsHelper { Dialog locationOffDialog ) { PermissionUtils.checkPermissionsAndPerformAction(activity, - permission.ACCESS_FINE_LOCATION, + new String[]{permission.ACCESS_FINE_LOCATION}, () -> { if(!isLocationAccessToAppsTurnedOn()) { showLocationOffDialog(locationOffDialog); diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index b84c4b447..8b7cf26c3 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -362,15 +362,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements @OnClick(R.id.mediaDetailImageViewSpacer) public void launchZoomActivity(final View view) { - final boolean permission = PermissionUtils. - hasPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE); - - if (permission) { + final boolean hasPermission = PermissionUtils.hasPermission(getActivity(), PermissionUtils.PERMISSIONS_STORAGE); + if (hasPermission) { launchZoomActivityAfterPermissionCheck(view); - } - else { + } else { PermissionUtils.checkPermissionsAndPerformAction(getActivity(), - Manifest.permission.READ_EXTERNAL_STORAGE, + PermissionUtils.PERMISSIONS_STORAGE, () -> { launchZoomActivityAfterPermissionCheck(view); }, @@ -1076,7 +1073,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements } else if (requestCode == REQUEST_CODE && resultCode == RESULT_CANCELED) { viewUtil.showShortToast(getContext(), - Objects.requireNonNull(getContext()) + requireContext() .getString(R.string.coordinates_picking_unsuccessful)); } else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_CANCELED) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java index 418880b0a..ba78d041c 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java @@ -441,7 +441,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment private void performMapReadyActions() { if (((MainActivity)getActivity()).activeFragment == ActiveFragment.NEARBY && isMapBoxReady) { if(!applicationKvStore.getBoolean("doNotAskForLocationPermission", false) || - PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)){ + PermissionUtils.hasPermission(getActivity(), new String[]{Manifest.permission.ACCESS_FINE_LOCATION})){ checkPermissionsAndPerformAction(); }else{ isPermissionDenied = true; @@ -1215,8 +1215,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment public void checkPermissionsAndPerformAction() { Timber.d("Checking permission and perfoming action"); PermissionUtils.checkPermissionsAndPerformAction(getActivity(), - Manifest.permission.ACCESS_FINE_LOCATION, - () -> locationPermissionGranted(), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + this::locationPermissionGranted, () -> isPermissionDenied = true, R.string.location_permission_title, R.string.location_permission_rationale_nearby); diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index fddb91df8..2efedbd0c 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -29,7 +29,11 @@ import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; import androidx.recyclerview.widget.RecyclerView.Adapter; import com.karumi.dexter.Dexter; +import com.karumi.dexter.MultiplePermissionsReport; +import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionGrantedResponse; +import com.karumi.dexter.listener.PermissionRequest; +import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import com.karumi.dexter.listener.single.BasePermissionListener; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; @@ -371,7 +375,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { Locale defLocale = new Locale(languageCode); if(keyListPreference.equals("appUiDefaultLanguagePref")) { appUiLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale)); - setLocale(Objects.requireNonNull(getActivity()), languageCode); + setLocale(requireActivity(), languageCode); getActivity().recreate(); final Intent intent = new Intent(getActivity(), MainActivity.class); startActivity(intent); @@ -410,8 +414,8 @@ public class SettingsFragment extends PreferenceFragmentCompat { separator.setVisibility(View.VISIBLE); final RecentLanguagesAdapter recentLanguagesAdapter = new RecentLanguagesAdapter( - getActivity(), - recentLanguagesDao.getRecentLanguages(), + getActivity(), + recentLanguagesDao.getRecentLanguages(), selectedLanguages); languageHistoryListView.setAdapter(recentLanguagesAdapter); } @@ -436,7 +440,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { final Locale defLocale = new Locale(recentLanguageCode); if (keyListPreference.equals("appUiDefaultLanguagePref")) { appUiLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale)); - setLocale(Objects.requireNonNull(getActivity()), recentLanguageCode); + setLocale(requireActivity(), recentLanguageCode); getActivity().recreate(); final Intent intent = new Intent(getActivity(), MainActivity.class); startActivity(intent); @@ -506,7 +510,7 @@ public class SettingsFragment extends PreferenceFragmentCompat { * First checks for external storage permissions and then sends logs via email */ private void checkPermissionsAndSendLogs() { - if (PermissionUtils.hasPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + if (PermissionUtils.hasPermission(getActivity(), PermissionUtils.PERMISSIONS_STORAGE)) { commonsLogSender.send(getActivity(), null); } else { requestExternalStoragePermissions(); @@ -514,16 +518,26 @@ public class SettingsFragment extends PreferenceFragmentCompat { } /** - * Requests external storage permissions and shows a toast stating that log collection has started + * Requests external storage permissions and shows a toast stating that log collection has + * started */ private void requestExternalStoragePermissions() { Dexter.withActivity(getActivity()) - .withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) - .withListener(new BasePermissionListener() { - @Override - public void onPermissionGranted(PermissionGrantedResponse response) { - ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.log_collection_started)); - } - }).check(); + .withPermissions(PermissionUtils.PERMISSIONS_STORAGE) + .withListener(new MultiplePermissionsListener() { + @Override + public void onPermissionsChecked(MultiplePermissionsReport report) { + ViewUtil.showLongToast(getActivity(), + getResources().getString(R.string.log_collection_started)); + } + + @Override + public void onPermissionRationaleShouldBeShown( + List permissions, PermissionToken token) { + + } + }) + .onSameThread() + .check(); } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 91ab805f3..ab6fa7035 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.upload; import static fr.free.nrw.commons.contributions.ContributionController.ACTION_INTERNAL_UPLOADS; +import static fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import android.Manifest; @@ -56,6 +57,7 @@ import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import java.security.Permission; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -146,12 +148,11 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, compositeDisposable = new CompositeDisposable(); init(); nearbyPopupAnswers = new HashMap<>(); - PermissionUtils.checkPermissionsAndPerformAction(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - this::receiveSharedItems, - R.string.storage_permission_title, - R.string.write_storage_permission_rationale_for_image_share); + PERMISSIONS_STORAGE, + this::receiveSharedItems, + R.string.storage_permission_title, + R.string.write_storage_permission_rationale_for_image_share); //getting the current dpi of the device and if it is less than 320dp i.e. overlapping //threshold, thumbnails automatically minimizes DisplayMetrics metrics = getResources().getDisplayMetrics(); @@ -159,7 +160,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, if (dpi<=321) { onRlContainerTitleClicked(); } - if (PermissionUtils.hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)) { + if (PermissionUtils.hasPermission(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { locationManager.registerLocationManager(); } locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); @@ -181,7 +182,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, private void initThumbnailsRecyclerView() { rvThumbnails.setLayoutManager(new LinearLayoutManager(this, - LinearLayoutManager.HORIZONTAL, false)); + LinearLayoutManager.HORIZONTAL, false)); thumbnailsAdapter = new ThumbnailsAdapter(() -> currentSelectedPosition); rvThumbnails.setAdapter(thumbnailsAdapter); @@ -193,7 +194,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, vpUpload.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, - int positionOffsetPixels) { + int positionOffsetPixels) { } @@ -238,29 +239,31 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, */ protected void checkBlockStatus() { compositeDisposable.add(userClient.isUserBlockedFromCommons() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .filter(result -> result) - .subscribe(result -> DialogUtil.showAlertDialog( - this, - getString(R.string.block_notification_title), - getString(R.string.block_notification), - getString(R.string.ok), - this::finish, - true))); + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .filter(result -> result) + .subscribe(result -> DialogUtil.showAlertDialog( + this, + getString(R.string.block_notification_title), + getString(R.string.block_notification), + getString(R.string.ok), + this::finish, + true))); } private void checkStoragePermissions() { - PermissionUtils.checkPermissionsAndPerformAction(this, - Manifest.permission.WRITE_EXTERNAL_STORAGE, + final boolean hasAllPermissions = PermissionUtils.hasPermission(this, PERMISSIONS_STORAGE); + if (!hasAllPermissions) { + PermissionUtils.checkPermissionsAndPerformAction(this, + PERMISSIONS_STORAGE, () -> { //TODO handle this }, R.string.storage_permission_title, R.string.write_storage_permission_rationale_for_image_share); + } } - @Override protected void onStop() { super.onStop(); @@ -328,7 +331,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, @Override public void updateTopCardTitle() { tvTopCardTitle.setText(getResources() - .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); + .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); } @Override @@ -370,13 +373,13 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, } else { //Show thumbnails if (uploadableFiles.size() - > 1) {//If there is only file, no need to show the image thumbnails + > 1) {//If there is only file, no need to show the image thumbnails thumbnailsAdapter.setUploadableFiles(uploadableFiles); } else { llContainerTopCard.setVisibility(View.GONE); } tvTopCardTitle.setText(getResources() - .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); + .getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size())); fragments = new ArrayList<>(); /* Suggest users to turn battery optimisation off when uploading more than a few files. @@ -419,7 +422,7 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, UploadMediaDetailFragment uploadMediaDetailFragment = new UploadMediaDetailFragment(); LocationPermissionsHelper locationPermissionsHelper = new LocationPermissionsHelper( - this, locationManager, null); + this, locationManager, null); if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { currLocation = locationManager.getLastLocation(); } @@ -525,8 +528,8 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, } float[] distance = new float[2]; Location.distanceBetween( - currLocation.getLatitude(), currLocation.getLongitude(), - prevLocation.getLatitude(), prevLocation.getLongitude(), distance); + currLocation.getLatitude(), currLocation.getLongitude(), + prevLocation.getLatitude(), prevLocation.getLongitude(), distance); return distance[0]; } @@ -670,5 +673,4 @@ public class UploadActivity extends BaseActivity implements UploadContract.View, this::finish ); } - } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt index d7d5ae3aa..480823d1f 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/DownloadUtils.kt @@ -38,7 +38,7 @@ object DownloadUtils { } PermissionUtils.checkPermissionsAndPerformAction( activity, - permission.WRITE_EXTERNAL_STORAGE, + PermissionUtils.PERMISSIONS_STORAGE, { enqueueRequest(activity, req) }, { Toast.makeText( diff --git a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java index 1b22fcfc0..d98bb6c86 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java @@ -1,121 +1,143 @@ package fr.free.nrw.commons.utils; +import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Build; import android.provider.Settings; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import com.karumi.dexter.Dexter; +import com.karumi.dexter.MultiplePermissionsReport; import com.karumi.dexter.PermissionToken; -import com.karumi.dexter.listener.PermissionDeniedResponse; -import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; -import com.karumi.dexter.listener.single.BasePermissionListener; +import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; +import java.util.List; public class PermissionUtils { + public static String[] PERMISSIONS_STORAGE = isSDKVersionScopedStorageCompatible() ? + isSDKVersionTiramisu() ? new String[]{ + Manifest.permission.READ_MEDIA_IMAGES} : + new String[]{Manifest.permission.READ_EXTERNAL_STORAGE} + : isSDKVersionTiramisu() ? new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_MEDIA_IMAGES} + : new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE}; + + private static boolean isSDKVersionScopedStorageCompatible() { + return Build.VERSION.SDK_INT > Build.VERSION_CODES.P; + } + + public static boolean isSDKVersionTiramisu() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU; + } + /** - * This method can be used by any activity which requires a permission which has been blocked(marked never ask again by the user) - It open the app settings from where the user can manually give us the required permission. + * This method can be used by any activity which requires a permission which has been + * blocked(marked never ask again by the user) It open the app settings from where the user can + * manually give us the required permission. + * * @param activity */ private static void askUserToManuallyEnablePermissionFromSettings(Activity activity) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", activity.getPackageName(), null); intent.setData(uri); - activity.startActivityForResult(intent,CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS); + activity.startActivityForResult(intent, + CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS); } /** * Checks whether the app already has a particular permission * * @param activity - * @param permission permission to be checked + * @param permissions permissions to be checked * @return */ - public static boolean hasPermission(Activity activity, String permission) { - return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_GRANTED; - + public static boolean hasPermission(Activity activity, String permissions[]) { + boolean hasPermission = true; + for (String permission : permissions + ) { + hasPermission = hasPermission && + ContextCompat.checkSelfPermission(activity, permission) + == PackageManager.PERMISSION_GRANTED; + } + return hasPermission; } /** - * Checks for a particular permission and runs the runnable to perform an action when the permission is granted - * Also, it shows a rationale if needed - * - * rationaleTitle and rationaleMessage can be invalid @StringRes. If the value is -1 then no permission rationale - * will be displayed and permission would be requested - * + * Checks for a particular permission and runs the runnable to perform an action when the + * permission is granted Also, it shows a rationale if needed + *

+ * rationaleTitle and rationaleMessage can be invalid @StringRes. If the value is -1 then no + * permission rationale will be displayed and permission would be requested + *

* Sample usage: - * - * PermissionUtils.checkPermissionsAndPerformAction(activity, - * Manifest.permission.WRITE_EXTERNAL_STORAGE, - * () -> initiateCameraUpload(activity), - * R.string.storage_permission_title, - * R.string.write_storage_permission_rationale); - * + *

+ * PermissionUtils.checkPermissionsAndPerformAction(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, + * () -> initiateCameraUpload(activity), R.string.storage_permission_title, + * R.string.write_storage_permission_rationale); + *

* If you don't want the permission rationale to be shown then use: + *

+ * PermissionUtils.checkPermissionsAndPerformAction(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, + * () -> initiateCameraUpload(activity), - 1, -1); * - * PermissionUtils.checkPermissionsAndPerformAction(activity, - * Manifest.permission.WRITE_EXTERNAL_STORAGE, - * () -> initiateCameraUpload(activity), - * - 1, -1); - * - * - * - * @param activity activity requesting permissions - * @param permission the permission being requests + * @param activity activity requesting permissions + * @param permissions the permissions array being requests * @param onPermissionGranted the runnable to be executed when the permission is granted - * @param rationaleTitle rationale title to be displayed when permission was denied. It can be an invalid @StringRes - * @param rationaleMessage rationale message to be displayed when permission was denied. It can be an invalid @StringRes + * @param rationaleTitle rationale title to be displayed when permission was denied. It can + * be an invalid @StringRes + * @param rationaleMessage rationale message to be displayed when permission was denied. It + * can be an invalid @StringRes */ - public static void checkPermissionsAndPerformAction(Activity activity, String permission, + public static void checkPermissionsAndPerformAction(Activity activity, String[] permissions, Runnable onPermissionGranted, @StringRes int rationaleTitle, @StringRes int rationaleMessage) { - checkPermissionsAndPerformAction(activity, permission, onPermissionGranted, null, + checkPermissionsAndPerformAction(activity, permissions, onPermissionGranted, null, rationaleTitle, rationaleMessage); } /** - * Checks for a particular permission and runs the corresponding runnables to perform an action when the permission is granted/denied - * Also, it shows a rationale if needed - * + * Checks for a particular permission and runs the corresponding runnables to perform an action + * when the permission is granted/denied Also, it shows a rationale if needed + *

* Sample usage: + *

+ * PermissionUtils.checkPermissionsAndPerformAction(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE, + * () -> initiateCameraUpload(activity), () -> showMessage(), R.string.storage_permission_title, + * R.string.write_storage_permission_rationale); * - * PermissionUtils.checkPermissionsAndPerformAction(activity, - * Manifest.permission.WRITE_EXTERNAL_STORAGE, - * () -> initiateCameraUpload(activity), - * () -> showMessage(), - * R.string.storage_permission_title, - * R.string.write_storage_permission_rationale); - * - * - * @param activity activity requesting permissions - * @param permission the permission being requests + * @param activity activity requesting permissions + * @param permissions the permissions array being requested * @param onPermissionGranted the runnable to be executed when the permission is granted - * @param onPermissionDenied the runnable to be executed when the permission is denied(but not permanently) - * @param rationaleTitle rationale title to be displayed when permission was denied - * @param rationaleMessage rationale message to be displayed when permission was denied + * @param onPermissionDenied the runnable to be executed when the permission is denied(but not + * permanently) + * @param rationaleTitle rationale title to be displayed when permission was denied + * @param rationaleMessage rationale message to be displayed when permission was denied */ - public static void checkPermissionsAndPerformAction(Activity activity, String permission, + public static void checkPermissionsAndPerformAction(Activity activity, String[] permissions, Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle, @StringRes int rationaleMessage) { Dexter.withActivity(activity) - .withPermission(permission) - .withListener(new BasePermissionListener() { - @Override public void onPermissionGranted(PermissionGrantedResponse response) { - onPermissionGranted.run(); - } - - @Override public void onPermissionDenied(PermissionDeniedResponse response) { - if (response.isPermanentlyDenied()) { + .withPermissions(permissions) + .withListener(new MultiplePermissionsListener() { + @Override + public void onPermissionsChecked(MultiplePermissionsReport report) { + if (report.areAllPermissionsGranted()) { + onPermissionGranted.run(); + } + if (report.isAnyPermissionPermanentlyDenied()) { + // permission is denied permanently, we will show user a dialog message. DialogUtil.showAlertDialog(activity, activity.getString(rationaleTitle), activity.getString(rationaleMessage), activity.getString(R.string.navigation_item_settings), null, @@ -128,7 +150,7 @@ public class PermissionUtils { } @Override - public void onPermissionRationaleShouldBeShown(PermissionRequest permission, + public void onPermissionRationaleShouldBeShown(List permissions, PermissionToken token) { if (rationaleTitle == -1 && rationaleMessage == -1) { token.continuePermissionRequest(); @@ -144,7 +166,7 @@ public class PermissionUtils { false); } }) + .onSameThread() .check(); } - }