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 6f95581b5..4d68500ca 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 @@ -5,22 +5,10 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.PackageManager; import android.database.Cursor; import android.database.DataSetObserver; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.CursorLoader; -import androidx.loader.content.Loader; -import androidx.cursoradapter.widget.CursorAdapter; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -28,6 +16,16 @@ import android.widget.Adapter; import android.widget.CheckBox; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.cursoradapter.widget.CursorAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.CursorLoader; +import androidx.loader.content.Loader; + import java.util.ArrayList; import javax.inject.Inject; @@ -57,6 +55,7 @@ import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.upload.UploadService; import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.DialogUtil; +import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -66,7 +65,7 @@ import timber.log.Timber; import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI; -import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST; +import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION; import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; @@ -351,35 +350,6 @@ public class ContributionsFragment } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Timber.d("onRequestPermissionsResult"); - switch (requestCode) { - case LOCATION_REQUEST: { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Timber.d("Location permission granted, refreshing view"); - // No need to display permission request button anymore - locationManager.registerLocationManager(); - } else { - if (store.getBoolean("displayLocationPermissionForCardView", true)) { - // Still ask for permission - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableLocationPermission, - checkBoxView, - false); - } - } - } - break; - - default: - // This is needed to allow the request codes from the Fragments to be routed appropriately - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - } - } /** * Replace whatever is in the current contributionsFragmentContainer view with * mediaDetailPagerFragment, and preserve previous state in back stack. @@ -496,7 +466,7 @@ public class ContributionsFragment if (store.getBoolean("displayNearbyCardView", true)) { - checkGPS(); + checkPermissionsAndShowNearbyCardView(); if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { nearbyNotificationCardView.setVisibility(View.VISIBLE); } @@ -509,77 +479,39 @@ public class ContributionsFragment fetchCampaigns(); } - /** - * Check GPS to decide displaying request permission button or not. - */ - private void checkGPS() { - if (!locationManager.isProviderEnabled()) { - Timber.d("GPS is not enabled"); - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_GPS; - if (store.getBoolean("displayLocationPermissionForCardView", true)) { - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableGPS, - checkBoxView, - false); - } - } else { - Timber.d("GPS is enabled"); - checkLocationPermission(); + private void checkPermissionsAndShowNearbyCardView() { + if (PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) { + onLocationPermissionGranted(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + && store.getBoolean("displayLocationPermissionForCardView", true) + && (((MainActivity) getActivity()).viewPager.getCurrentItem() == CONTRIBUTIONS_TAB_POSITION)) { + nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; + showNearbyCardPermissionRationale(); } } - private void checkLocationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; - locationManager.registerLocationManager(); - } else { - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; - // If user didn't selected Don't ask again - if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) - && store.getBoolean("displayLocationPermissionForCardView", true)) { - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableLocationPermission, - checkBoxView, - false); - } - } - } else { - // If device is under Marshmallow, we already checked for GPS - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; - locationManager.registerLocationManager(); - } + private void requestLocationPermission() { + PermissionUtils.checkPermissionsAndPerformAction(getActivity(), + Manifest.permission.ACCESS_FINE_LOCATION, + this::onLocationPermissionGranted, + this::displayYouWontSeeNearbyMessage, + -1, + -1); } - private void enableLocationPermission() { - if (!getActivity().isFinishing()) { - ((MainActivity) getActivity()).locationManager.requestPermissions(getActivity()); - } + private void onLocationPermissionGranted() { + nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; + locationManager.registerLocationManager(); } - private void enableGPS() { - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.gps_disabled) - .setCancelable(false) - .setPositiveButton(R.string.enable_gps, - (dialog, id) -> { - Intent callGPSSettingIntent = new Intent( - android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - Timber.d("Loaded settings page"); - ((MainActivity) getActivity()).startActivityForResult(callGPSSettingIntent, 1); - }) - .setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> { - dialog.cancel(); - displayYouWontSeeNearbyMessage(); - }) - .create() - .show(); + private void showNearbyCardPermissionRationale() { + DialogUtil.showAlertDialog(getActivity(), + getString(R.string.nearby_card_permission_title), + getString(R.string.nearby_card_permission_explanation), + this::displayYouWontSeeNearbyMessage, + this::requestLocationPermission, + checkBoxView, + false); } private void displayYouWontSeeNearbyMessage() { @@ -589,7 +521,6 @@ public class ContributionsFragment private void updateClosestNearbyCardViewInfo() { curLatLng = locationManager.getLastLocation(); - compositeDisposable.add(Observable.fromCallable(() -> nearbyController .loadAttractionsFromLocation(curLatLng, curLatLng, true, false)) // thanks to boolean, it will only return closest result .subscribeOn(Schedulers.io()) 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 e7312edb8..698057935 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 @@ -45,7 +45,6 @@ import io.reactivex.schedulers.Schedulers; import timber.log.Timber; import static android.content.ContentResolver.requestSync; -import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST; public class MainActivity extends AuthenticatedActivity implements FragmentManager.OnBackStackChangedListener { @@ -68,8 +67,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag public boolean isAuthCookieAcquired = false; public ContributionsActivityPagerAdapter contributionsActivityPagerAdapter; - public final int CONTRIBUTIONS_TAB_POSITION = 0; - public final int NEARBY_TAB_POSITION = 1; + public static final int CONTRIBUTIONS_TAB_POSITION = 0; + public static final int NEARBY_TAB_POSITION = 1; public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible private Menu menu; @@ -361,12 +360,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag } } - private boolean deviceHasCamera() { - PackageManager pm = getPackageManager(); - return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) || - pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); - } - public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter { FragmentManager fragmentManager; private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details @@ -450,15 +443,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag return "android:switcher:" + viewId + ":" + index; } - /** - * In first tab we can have ContributionsFragment or Media details fragment. This method - * is responsible to update related boolean - * @param isContributionsListFragment true when contribution fragment should be visible, false - * means user clicked to MediaDetails - */ - private void updateContributionFragmentTabContent(boolean isContributionsListFragment) { - this.isContributionsListFragment = isContributionsListFragment; - } } @Override @@ -468,27 +452,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag controller.handleActivityResult(this, requestCode, resultCode, data); } - @Override - public void onRequestPermissionsResult(int requestCode, - String[] permissions, int[] grantResults) { - if (requestCode == LOCATION_REQUEST) { - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Timber.d("Location permission given"); - ((ContributionsFragment)contributionsActivityPagerAdapter - .getItem(0)).locationManager.registerLocationManager(); - } else { - // If nearby fragment is visible and location permission is not given, send user back to contrib fragment - if (!isContributionsFragmentVisible) { - viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION); - - // TODO: If contrib fragment is visible and location permission is not given, display permission request button - } - } - } - } - @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java index 68b7c742d..8e6705ecb 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java @@ -1,19 +1,12 @@ package fr.free.nrw.commons.location; -import android.Manifest; -import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; -import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - import java.util.HashSet; import java.util.List; import java.util.Set; @@ -44,78 +37,6 @@ public class LocationServiceManager implements LocationListener { this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } - /** - * Returns the current status of the location provider. - * - * @return true if the location provider is enabled - */ - public boolean isProviderEnabled() { - return (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)); - } - - /** - * Returns whether the location permission is granted. - * @return true if the location permission is granted - */ - public boolean isLocationPermissionGranted(@NonNull Context context) { - return ContextCompat.checkSelfPermission(context, - Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; - } - - /** - * Requests the location permission to be granted. - * - * @param activity the activity - */ - public void requestPermissions(Activity activity) { - if (activity.isFinishing()) { - return; - } - ActivityCompat.requestPermissions(activity, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - LOCATION_REQUEST); - } - - /** - * The permission explanation dialog box is now displayed just once for a particular activity. We are subscribing - * to updates from multiple providers so its important to show the dialog just once. Otherwise it will be displayed - * once for every provider, which in our case currently is 2. - * @param activity - * @return - */ - public boolean isPermissionExplanationRequired(Activity activity) { - if (activity.isFinishing()) { - return false; - } - boolean showRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION); - if (showRequestPermissionRationale && !locationExplanationDisplayed.contains(activity)) { - locationExplanationDisplayed.add(activity); - return true; - } - return false; - } - - /** - * Gets the last known location in cases where there wasn't time to register a listener - * (e.g. when Location permission just granted) - * @return last known LatLng - */ - @SuppressLint("MissingPermission") - public LatLng getLKL(@NonNull Context context) { - if (isLocationPermissionGranted(context)) { - Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (lastKL == null) { - lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - } - if (lastKL == null) { - return null; - } - return LatLng.from(lastKL); - } else { - return null; - } - } - public LatLng getLastLocation() { if (lastLocation == null) { return null; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java index 100ca733b..062f7da90 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java @@ -1,20 +1,11 @@ package fr.free.nrw.commons.nearby; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Build; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.constraintlayout.widget.ConstraintLayout; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.snackbar.Snackbar; - -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,12 +13,19 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ProgressBar; +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.snackbar.Snackbar; import com.google.gson.Gson; import java.util.List; import javax.inject.Inject; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; @@ -38,6 +36,7 @@ import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.utils.FragmentUtils; import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.wikidata.WikidataEditListener; import io.reactivex.Observable; @@ -45,6 +44,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION; +import static fr.free.nrw.commons.contributions.MainActivity.NEARBY_TAB_POSITION; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; @@ -193,8 +194,8 @@ public class NearbyFragment extends CommonsDaggerSupportFragment bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override - public void onStateChanged(View bottomSheet, int newState) { - prepareViewsForSheetPosition(newState); + public void onStateChanged(View bottomSheet, int unusedNewState) { + prepareViewsForSheetPosition(); } @Override @@ -210,9 +211,8 @@ public class NearbyFragment extends CommonsDaggerSupportFragment /** * Sets camera position, zoom level according to sheet positions - * @param bottomSheetState expanded, collapsed or hidden */ - public void prepareViewsForSheetPosition(int bottomSheetState) { + private void prepareViewsForSheetPosition() { // TODO } @@ -246,7 +246,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment * * @param locationChangeType defines if location changed significantly or slightly */ - public void refreshView(LocationServiceManager.LocationChangeType locationChangeType) { + private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) { Timber.d("Refreshing nearby places"); if (lockNearbyView) { return; @@ -324,7 +324,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment * button. It populates places for custom location. * @param customLatLng Custom area which we will search around */ - public void refreshViewForCustomLocation(LatLng customLatLng, boolean refreshForCurrentLocation) { + void refreshViewForCustomLocation(LatLng customLatLng, boolean refreshForCurrentLocation) { if (customLatLng == null) { // If null, return return; @@ -568,130 +568,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment * This method first checks if the location permissions has been granted and then register the location manager for updates. */ private void registerLocationUpdates() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - locationManager.registerLocationManager(); - } else { - // Should we show an explanation? - if (locationManager.isPermissionExplanationRequired(getActivity())) { - new AlertDialog.Builder(getActivity()) - .setMessage(getString(R.string.location_permission_rationale_nearby)) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - requestLocationPermissions(); - dialog.dismiss(); - }) - .setNegativeButton(android.R.string.cancel, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - - } else { - // No explanation needed, we can request the permission. - requestLocationPermissions(); - } - } - } else { - locationManager.registerLocationManager(); - } - } - - /** - * Requests location permission if activity is not null - */ - private void requestLocationPermissions() { - if (!getActivity().isFinishing()) { - locationManager.requestPermissions(getActivity()); - } - } - - /** - * Will warn user if location is denied - */ - private void showLocationPermissionDeniedErrorDialog() { - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.nearby_needs_permissions) - .setCancelable(false) - .setPositiveButton(R.string.give_permission, (dialog, which) -> { - //will ask for the location permission again - checkGps(); - }) - .setNegativeButton(R.string.cancel, (dialog, which) -> { - //dismiss dialog and send user to contributions tab instead - dialog.cancel(); - ((MainActivity)getActivity()).viewPager.setCurrentItem(((MainActivity)getActivity()).CONTRIBUTIONS_TAB_POSITION); - }) - .create() - .show(); - } - - /** - * Checks device GPS permission first for all API levels - */ - private void checkGps() { - Timber.d("checking GPS"); - if (!locationManager.isProviderEnabled()) { - Timber.d("GPS is not enabled"); - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.gps_disabled) - .setCancelable(false) - .setPositiveButton(R.string.enable_gps, - (dialog, id) -> { - Intent callGPSSettingIntent = new Intent( - android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - Timber.d("Loaded settings page"); - startActivityForResult(callGPSSettingIntent, 1); - }) - .setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - } else { - Timber.d("GPS is enabled"); - checkLocationPermission(); - } - } - - /** - * This method ideally should be called from inside of CheckGPS method. If device GPS is enabled - * then we need to control app specific permissions for >=M devices. For other devices, enabled - * GPS is enough for nearby, so directly call refresh view. - */ - private void checkLocationPermission() { - Timber.d("Checking location permission"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - refreshView(LOCATION_SIGNIFICANTLY_CHANGED); - } else { - // Should we show an explanation? - if (locationManager.isPermissionExplanationRequired(getActivity())) { - // Show an explanation to the user *asynchronously* -- don't block - // this thread waiting for the user's response! After the user - // sees the explanation, try again to request the permission. - new AlertDialog.Builder(getActivity()) - .setMessage(getString(R.string.location_permission_rationale_nearby)) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - requestLocationPermissions(); - dialog.dismiss(); - }) - .setNegativeButton(android.R.string.cancel, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - - } else { - // No explanation needed, we can request the permission. - requestLocationPermissions(); - } - } - } else { - refreshView(LOCATION_SIGNIFICANTLY_CHANGED); - } + locationManager.registerLocationManager(); } private void showErrorMessage(String message) { @@ -748,8 +625,12 @@ public class NearbyFragment extends CommonsDaggerSupportFragment public void onResume() { super.onResume(); // Resume the fragment if exist - resumeFragment(); + if (((MainActivity) getActivity()).viewPager.getCurrentItem() == NEARBY_TAB_POSITION) { + checkPermissionsAndPerformAction(this::resumeFragment); + } else { + resumeFragment(); } + } /** * Perform nearby operations on nearby tab selected @@ -758,8 +639,16 @@ public class NearbyFragment extends CommonsDaggerSupportFragment public void onTabSelected(boolean onOrientationChanged) { Timber.d("On nearby tab selected"); this.onOrientationChanged = onOrientationChanged; - performNearbyOperations(); + checkPermissionsAndPerformAction(this::performNearbyOperations); + } + private void checkPermissionsAndPerformAction(Runnable runnable) { + PermissionUtils.checkPermissionsAndPerformAction(getActivity(), + Manifest.permission.ACCESS_FINE_LOCATION, + runnable, + () -> ((MainActivity) getActivity()).viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION), + R.string.location_permission_title, + R.string.location_permission_rationale_nearby); } /** @@ -769,8 +658,8 @@ public class NearbyFragment extends CommonsDaggerSupportFragment locationManager.addLocationListener(this); registerLocationUpdates(); lockNearbyView = false; - checkGps(); addNetworkBroadcastReceiver(); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } @Override 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 c4268790c..8d258256a 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 @@ -5,17 +5,17 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; -import androidx.annotation.StringRes; -import androidx.core.content.ContextCompat; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; + import com.karumi.dexter.Dexter; 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 fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; @@ -27,7 +27,7 @@ public class PermissionUtils { It open the app settings from where the user can manually give us the required permission. * @param activity */ - public static void askUserToManuallyEnablePermissionFromSettings(Activity 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); @@ -50,6 +50,9 @@ public class PermissionUtils { * 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, @@ -58,12 +61,20 @@ public class PermissionUtils { * 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); + * + * * * @param activity activity requesting permissions * @param permission the permission 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 - * @param rationaleMessage rationale message to be displayed when permission was denied + * @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, Runnable onPermissionGranted, @StringRes int rationaleTitle, @@ -93,7 +104,6 @@ public class PermissionUtils { * @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, Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle, @StringRes int rationaleMessage) { @@ -120,6 +130,10 @@ public class PermissionUtils { @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { + if (rationaleTitle == -1 && rationaleMessage == -1) { + token.continuePermissionRequest(); + return; + } DialogUtil.showAlertDialog(activity, activity.getString(rationaleTitle), activity.getString(rationaleMessage), activity.getString(android.R.string.ok), @@ -129,4 +143,5 @@ public class PermissionUtils { }) .check(); } + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 276f9e2c1..b460f358e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -166,6 +166,7 @@ Requesting Storage Permission Required permission: Read external storage. App cannot access your gallery without this. Required permission: Write external storage. App cannot access your camera/gallery without this. + Requesting Location Permission Optional permission: Get current location for category suggestions OK Nearby Places