mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
* Use dexter to ask for location permissions * Addressed code review comments
This commit is contained in:
parent
6f9d69e63c
commit
8971743479
6 changed files with 95 additions and 375 deletions
|
|
@ -5,22 +5,10 @@ import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
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.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -28,6 +16,16 @@ import android.widget.Adapter;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.Toast;
|
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 java.util.ArrayList;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
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.upload.UploadService;
|
||||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||||
import fr.free.nrw.commons.utils.DialogUtil;
|
import fr.free.nrw.commons.utils.DialogUtil;
|
||||||
|
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||||
import fr.free.nrw.commons.utils.ViewUtil;
|
import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
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.ContributionDao.Table.ALL_FIELDS;
|
||||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
|
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.settings.Prefs.UPLOADS_SHOWING;
|
||||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
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
|
* Replace whatever is in the current contributionsFragmentContainer view with
|
||||||
* mediaDetailPagerFragment, and preserve previous state in back stack.
|
* mediaDetailPagerFragment, and preserve previous state in back stack.
|
||||||
|
|
@ -496,7 +466,7 @@ public class ContributionsFragment
|
||||||
|
|
||||||
|
|
||||||
if (store.getBoolean("displayNearbyCardView", true)) {
|
if (store.getBoolean("displayNearbyCardView", true)) {
|
||||||
checkGPS();
|
checkPermissionsAndShowNearbyCardView();
|
||||||
if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
|
if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
|
||||||
nearbyNotificationCardView.setVisibility(View.VISIBLE);
|
nearbyNotificationCardView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
@ -509,77 +479,39 @@ public class ContributionsFragment
|
||||||
fetchCampaigns();
|
fetchCampaigns();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void checkPermissionsAndShowNearbyCardView() {
|
||||||
* Check GPS to decide displaying request permission button or not.
|
if (PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) {
|
||||||
*/
|
onLocationPermissionGranted();
|
||||||
private void checkGPS() {
|
} else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||||
if (!locationManager.isProviderEnabled()) {
|
&& store.getBoolean("displayLocationPermissionForCardView", true)
|
||||||
Timber.d("GPS is not enabled");
|
&& (((MainActivity) getActivity()).viewPager.getCurrentItem() == CONTRIBUTIONS_TAB_POSITION)) {
|
||||||
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_GPS;
|
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
|
||||||
if (store.getBoolean("displayLocationPermissionForCardView", true)) {
|
showNearbyCardPermissionRationale();
|
||||||
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 checkLocationPermission() {
|
private void requestLocationPermission() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
PermissionUtils.checkPermissionsAndPerformAction(getActivity(),
|
||||||
if (locationManager.isLocationPermissionGranted(requireContext())) {
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED;
|
this::onLocationPermissionGranted,
|
||||||
locationManager.registerLocationManager();
|
this::displayYouWontSeeNearbyMessage,
|
||||||
} else {
|
-1,
|
||||||
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
|
-1);
|
||||||
// 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 enableLocationPermission() {
|
private void onLocationPermissionGranted() {
|
||||||
if (!getActivity().isFinishing()) {
|
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED;
|
||||||
((MainActivity) getActivity()).locationManager.requestPermissions(getActivity());
|
locationManager.registerLocationManager();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableGPS() {
|
private void showNearbyCardPermissionRationale() {
|
||||||
new AlertDialog.Builder(getActivity())
|
DialogUtil.showAlertDialog(getActivity(),
|
||||||
.setMessage(R.string.gps_disabled)
|
getString(R.string.nearby_card_permission_title),
|
||||||
.setCancelable(false)
|
getString(R.string.nearby_card_permission_explanation),
|
||||||
.setPositiveButton(R.string.enable_gps,
|
this::displayYouWontSeeNearbyMessage,
|
||||||
(dialog, id) -> {
|
this::requestLocationPermission,
|
||||||
Intent callGPSSettingIntent = new Intent(
|
checkBoxView,
|
||||||
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
false);
|
||||||
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 displayYouWontSeeNearbyMessage() {
|
private void displayYouWontSeeNearbyMessage() {
|
||||||
|
|
@ -589,7 +521,6 @@ public class ContributionsFragment
|
||||||
|
|
||||||
private void updateClosestNearbyCardViewInfo() {
|
private void updateClosestNearbyCardViewInfo() {
|
||||||
curLatLng = locationManager.getLastLocation();
|
curLatLng = locationManager.getLastLocation();
|
||||||
|
|
||||||
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
|
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
|
||||||
.loadAttractionsFromLocation(curLatLng, curLatLng, true, false)) // thanks to boolean, it will only return closest result
|
.loadAttractionsFromLocation(curLatLng, curLatLng, true, false)) // thanks to boolean, it will only return closest result
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
import static android.content.ContentResolver.requestSync;
|
import static android.content.ContentResolver.requestSync;
|
||||||
import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST;
|
|
||||||
|
|
||||||
public class MainActivity extends AuthenticatedActivity implements FragmentManager.OnBackStackChangedListener {
|
public class MainActivity extends AuthenticatedActivity implements FragmentManager.OnBackStackChangedListener {
|
||||||
|
|
||||||
|
|
@ -68,8 +67,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
public boolean isAuthCookieAcquired = false;
|
public boolean isAuthCookieAcquired = false;
|
||||||
|
|
||||||
public ContributionsActivityPagerAdapter contributionsActivityPagerAdapter;
|
public ContributionsActivityPagerAdapter contributionsActivityPagerAdapter;
|
||||||
public final int CONTRIBUTIONS_TAB_POSITION = 0;
|
public static final int CONTRIBUTIONS_TAB_POSITION = 0;
|
||||||
public final int NEARBY_TAB_POSITION = 1;
|
public static final int NEARBY_TAB_POSITION = 1;
|
||||||
|
|
||||||
public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible
|
public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible
|
||||||
private Menu menu;
|
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 {
|
public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter {
|
||||||
FragmentManager fragmentManager;
|
FragmentManager fragmentManager;
|
||||||
private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details
|
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;
|
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
|
@Override
|
||||||
|
|
@ -468,27 +452,6 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag
|
||||||
controller.handleActivityResult(this, requestCode, resultCode, data);
|
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
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
package fr.free.nrw.commons.location;
|
package fr.free.nrw.commons.location;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.location.LocationListener;
|
import android.location.LocationListener;
|
||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.os.Bundle;
|
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.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -44,78 +37,6 @@ public class LocationServiceManager implements LocationListener {
|
||||||
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
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() {
|
public LatLng getLastLocation() {
|
||||||
if (lastLocation == null) {
|
if (lastLocation == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,11 @@
|
||||||
package fr.free.nrw.commons.nearby;
|
package fr.free.nrw.commons.nearby;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
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.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
@ -22,12 +13,19 @@ import android.widget.FrameLayout;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
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.BindView;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.R;
|
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.location.LocationUpdateListener;
|
||||||
import fr.free.nrw.commons.utils.FragmentUtils;
|
import fr.free.nrw.commons.utils.FragmentUtils;
|
||||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
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.utils.ViewUtil;
|
||||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||||
import io.reactivex.Observable;
|
import io.reactivex.Observable;
|
||||||
|
|
@ -45,6 +44,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import timber.log.Timber;
|
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_SIGNIFICANTLY_CHANGED;
|
||||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
|
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
|
||||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
|
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() {
|
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateChanged(View bottomSheet, int newState) {
|
public void onStateChanged(View bottomSheet, int unusedNewState) {
|
||||||
prepareViewsForSheetPosition(newState);
|
prepareViewsForSheetPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -210,9 +211,8 @@ public class NearbyFragment extends CommonsDaggerSupportFragment
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets camera position, zoom level according to sheet positions
|
* Sets camera position, zoom level according to sheet positions
|
||||||
* @param bottomSheetState expanded, collapsed or hidden
|
|
||||||
*/
|
*/
|
||||||
public void prepareViewsForSheetPosition(int bottomSheetState) {
|
private void prepareViewsForSheetPosition() {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -246,7 +246,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment
|
||||||
*
|
*
|
||||||
* @param locationChangeType defines if location changed significantly or slightly
|
* @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");
|
Timber.d("Refreshing nearby places");
|
||||||
if (lockNearbyView) {
|
if (lockNearbyView) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -324,7 +324,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment
|
||||||
* button. It populates places for custom location.
|
* button. It populates places for custom location.
|
||||||
* @param customLatLng Custom area which we will search around
|
* @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 (customLatLng == null) {
|
||||||
// If null, return
|
// If null, return
|
||||||
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.
|
* This method first checks if the location permissions has been granted and then register the location manager for updates.
|
||||||
*/
|
*/
|
||||||
private void registerLocationUpdates() {
|
private void registerLocationUpdates() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
locationManager.registerLocationManager();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showErrorMessage(String message) {
|
private void showErrorMessage(String message) {
|
||||||
|
|
@ -748,8 +625,12 @@ public class NearbyFragment extends CommonsDaggerSupportFragment
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
// Resume the fragment if exist
|
// 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
|
* Perform nearby operations on nearby tab selected
|
||||||
|
|
@ -758,8 +639,16 @@ public class NearbyFragment extends CommonsDaggerSupportFragment
|
||||||
public void onTabSelected(boolean onOrientationChanged) {
|
public void onTabSelected(boolean onOrientationChanged) {
|
||||||
Timber.d("On nearby tab selected");
|
Timber.d("On nearby tab selected");
|
||||||
this.onOrientationChanged = onOrientationChanged;
|
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);
|
locationManager.addLocationListener(this);
|
||||||
registerLocationUpdates();
|
registerLocationUpdates();
|
||||||
lockNearbyView = false;
|
lockNearbyView = false;
|
||||||
checkGps();
|
|
||||||
addNetworkBroadcastReceiver();
|
addNetworkBroadcastReceiver();
|
||||||
|
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,17 @@ import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
import com.karumi.dexter.Dexter;
|
import com.karumi.dexter.Dexter;
|
||||||
import com.karumi.dexter.PermissionToken;
|
import com.karumi.dexter.PermissionToken;
|
||||||
import com.karumi.dexter.listener.PermissionDeniedResponse;
|
import com.karumi.dexter.listener.PermissionDeniedResponse;
|
||||||
import com.karumi.dexter.listener.PermissionGrantedResponse;
|
import com.karumi.dexter.listener.PermissionGrantedResponse;
|
||||||
import com.karumi.dexter.listener.PermissionRequest;
|
import com.karumi.dexter.listener.PermissionRequest;
|
||||||
import com.karumi.dexter.listener.single.BasePermissionListener;
|
import com.karumi.dexter.listener.single.BasePermissionListener;
|
||||||
|
|
||||||
import fr.free.nrw.commons.CommonsApplication;
|
import fr.free.nrw.commons.CommonsApplication;
|
||||||
import fr.free.nrw.commons.R;
|
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.
|
It open the app settings from where the user can manually give us the required permission.
|
||||||
* @param activity
|
* @param activity
|
||||||
*/
|
*/
|
||||||
public static void askUserToManuallyEnablePermissionFromSettings(Activity activity) {
|
private static void askUserToManuallyEnablePermissionFromSettings(Activity activity) {
|
||||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
||||||
intent.setData(uri);
|
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
|
* 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
|
* 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:
|
* Sample usage:
|
||||||
*
|
*
|
||||||
* PermissionUtils.checkPermissionsAndPerformAction(activity,
|
* PermissionUtils.checkPermissionsAndPerformAction(activity,
|
||||||
|
|
@ -58,12 +61,20 @@ public class PermissionUtils {
|
||||||
* R.string.storage_permission_title,
|
* R.string.storage_permission_title,
|
||||||
* R.string.write_storage_permission_rationale);
|
* 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 activity activity requesting permissions
|
||||||
* @param permission the permission being requests
|
* @param permission the permission being requests
|
||||||
* @param onPermissionGranted the runnable to be executed when the permission is granted
|
* @param onPermissionGranted the runnable to be executed when the permission is granted
|
||||||
* @param rationaleTitle rationale title 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
|
* @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 permission,
|
||||||
Runnable onPermissionGranted, @StringRes int rationaleTitle,
|
Runnable onPermissionGranted, @StringRes int rationaleTitle,
|
||||||
|
|
@ -93,7 +104,6 @@ public class PermissionUtils {
|
||||||
* @param rationaleTitle rationale title to be displayed when permission was denied
|
* @param rationaleTitle rationale title to be displayed when permission was denied
|
||||||
* @param rationaleMessage rationale message 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 permission,
|
||||||
Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle,
|
Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle,
|
||||||
@StringRes int rationaleMessage) {
|
@StringRes int rationaleMessage) {
|
||||||
|
|
@ -120,6 +130,10 @@ public class PermissionUtils {
|
||||||
@Override
|
@Override
|
||||||
public void onPermissionRationaleShouldBeShown(PermissionRequest permission,
|
public void onPermissionRationaleShouldBeShown(PermissionRequest permission,
|
||||||
PermissionToken token) {
|
PermissionToken token) {
|
||||||
|
if (rationaleTitle == -1 && rationaleMessage == -1) {
|
||||||
|
token.continuePermissionRequest();
|
||||||
|
return;
|
||||||
|
}
|
||||||
DialogUtil.showAlertDialog(activity, activity.getString(rationaleTitle),
|
DialogUtil.showAlertDialog(activity, activity.getString(rationaleTitle),
|
||||||
activity.getString(rationaleMessage),
|
activity.getString(rationaleMessage),
|
||||||
activity.getString(android.R.string.ok),
|
activity.getString(android.R.string.ok),
|
||||||
|
|
@ -129,4 +143,5 @@ public class PermissionUtils {
|
||||||
})
|
})
|
||||||
.check();
|
.check();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@
|
||||||
<string name="storage_permission_title">Requesting Storage Permission</string>
|
<string name="storage_permission_title">Requesting Storage Permission</string>
|
||||||
<string name="read_storage_permission_rationale">Required permission: Read external storage. App cannot access your gallery without this.</string>
|
<string name="read_storage_permission_rationale">Required permission: Read external storage. App cannot access your gallery without this.</string>
|
||||||
<string name="write_storage_permission_rationale">Required permission: Write external storage. App cannot access your camera/gallery without this.</string>
|
<string name="write_storage_permission_rationale">Required permission: Write external storage. App cannot access your camera/gallery without this.</string>
|
||||||
|
<string name="location_permission_title">Requesting Location Permission</string>
|
||||||
<string name="location_permission_rationale">Optional permission: Get current location for category suggestions</string>
|
<string name="location_permission_rationale">Optional permission: Get current location for category suggestions</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="title_activity_nearby">Nearby Places</string>
|
<string name="title_activity_nearby">Nearby Places</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue