mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
Fix conflixts
This commit is contained in:
commit
2e1cb07a51
26 changed files with 426 additions and 533 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(getActivity());
|
|
||||||
} 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(getActivity());
|
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(getActivity());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableLocationPermission() {
|
private void onLocationPermissionGranted() {
|
||||||
if (!getActivity().isFinishing()) {
|
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED;
|
||||||
((MainActivity) getActivity()).locationManager.requestPermissions(getActivity());
|
locationManager.registerLocationManager(getActivity());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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())
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,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 {
|
||||||
|
|
||||||
|
|
@ -69,8 +68,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;
|
||||||
|
|
@ -362,12 +361,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
|
||||||
|
|
@ -451,15 +444,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
|
||||||
|
|
@ -469,26 +453,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(this);
|
|
||||||
} 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() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
setNotificationCount();
|
setNotificationCount();
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,12 @@ package fr.free.nrw.commons.di;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.collection.LruCache;
|
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import org.wikipedia.dataclient.WikiSite;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -16,6 +17,7 @@ import java.util.Map;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import androidx.collection.LruCache;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import fr.free.nrw.commons.BuildConfig;
|
import fr.free.nrw.commons.BuildConfig;
|
||||||
|
|
@ -24,7 +26,6 @@ import fr.free.nrw.commons.auth.AccountUtil;
|
||||||
import fr.free.nrw.commons.auth.SessionManager;
|
import fr.free.nrw.commons.auth.SessionManager;
|
||||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|
||||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||||
import fr.free.nrw.commons.settings.Prefs;
|
import fr.free.nrw.commons.settings.Prefs;
|
||||||
import fr.free.nrw.commons.upload.UploadController;
|
import fr.free.nrw.commons.upload.UploadController;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import android.content.Context;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import org.wikipedia.dataclient.ServiceFactory;
|
||||||
|
import org.wikipedia.dataclient.WikiSite;
|
||||||
import org.wikipedia.json.GsonUtil;
|
import org.wikipedia.json.GsonUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
@ -20,6 +22,7 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||||
|
import fr.free.nrw.commons.review.ReviewInterface;
|
||||||
import okhttp3.Cache;
|
import okhttp3.Cache;
|
||||||
import okhttp3.HttpUrl;
|
import okhttp3.HttpUrl;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
|
@ -108,4 +111,16 @@ public class NetworkingModule {
|
||||||
return GsonUtil.getDefaultGson();
|
return GsonUtil.getDefaultGson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
@Named("commons-wikisite")
|
||||||
|
public WikiSite provideCommonsWikiSite() {
|
||||||
|
return new WikiSite(BuildConfig.COMMONS_URL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
public ReviewInterface provideReviewInterface(@Named("commons-wikisite") WikiSite commonsWikiSite) {
|
||||||
|
return ServiceFactory.get(commonsWikiSite, BuildConfig.COMMONS_URL, ReviewInterface.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,13 @@
|
||||||
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 android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
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;
|
||||||
|
|
@ -45,78 +38,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,6 +1,7 @@
|
||||||
package fr.free.nrw.commons.mwapi;
|
package fr.free.nrw.commons.mwapi;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
|
@ -10,8 +11,6 @@ import com.google.gson.reflect.TypeToken;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||||
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||||
import org.wikipedia.dataclient.mwapi.RecentChange;
|
|
||||||
import org.wikipedia.util.DateUtil;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
|
@ -20,7 +19,6 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
@ -86,7 +84,7 @@ public class OkHttpJsonApiClient {
|
||||||
public Single<Integer> getUploadCount(String userName) {
|
public Single<Integer> getUploadCount(String userName) {
|
||||||
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
||||||
urlBuilder
|
urlBuilder
|
||||||
.addPathSegments("/uploadsbyuser.py")
|
.addPathSegments("uploadsbyuser.py")
|
||||||
.addQueryParameter("user", userName);
|
.addQueryParameter("user", userName);
|
||||||
|
|
||||||
if (ConfigUtils.isBetaFlavour()) {
|
if (ConfigUtils.isBetaFlavour()) {
|
||||||
|
|
@ -112,7 +110,7 @@ public class OkHttpJsonApiClient {
|
||||||
public Single<Integer> getWikidataEdits(String userName) {
|
public Single<Integer> getWikidataEdits(String userName) {
|
||||||
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();
|
||||||
urlBuilder
|
urlBuilder
|
||||||
.addPathSegments("/wikidataedits.py")
|
.addPathSegments("wikidataedits.py")
|
||||||
.addQueryParameter("user", userName);
|
.addQueryParameter("user", userName);
|
||||||
|
|
||||||
if (ConfigUtils.isBetaFlavour()) {
|
if (ConfigUtils.isBetaFlavour()) {
|
||||||
|
|
@ -132,7 +130,9 @@ public class OkHttpJsonApiClient {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
GetWikidataEditCountResponse countResponse = gson.fromJson(json, GetWikidataEditCountResponse.class);
|
GetWikidataEditCountResponse countResponse = gson.fromJson(json, GetWikidataEditCountResponse.class);
|
||||||
return countResponse.getWikidataEditCount();
|
if (null != countResponse) {
|
||||||
|
return countResponse.getWikidataEditCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
@ -421,80 +421,4 @@ public class OkHttpJsonApiClient {
|
||||||
private Map<String, String> getContinueValues(String keyword) {
|
private Map<String, String> getContinueValues(String keyword) {
|
||||||
return defaultKvStore.getJson("query_continue_" + keyword, mapType);
|
return defaultKvStore.getJson("query_continue_" + keyword, mapType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns recent changes on commons
|
|
||||||
*
|
|
||||||
* @return list of recent changes made
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public Single<List<RecentChange>> getRecentFileChanges() {
|
|
||||||
final int RANDOM_SECONDS = 60 * 60 * 24 * 30;
|
|
||||||
final String FILE_NAMESPACE = "6";
|
|
||||||
Random r = new Random();
|
|
||||||
Date now = new Date();
|
|
||||||
Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L);
|
|
||||||
|
|
||||||
String rcStart = DateUtil.iso8601DateFormat(startDate);
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(commonsBaseUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("action", "query")
|
|
||||||
.addQueryParameter("format", "json")
|
|
||||||
.addQueryParameter("formatversion", "2")
|
|
||||||
.addQueryParameter("list", "recentchanges")
|
|
||||||
.addQueryParameter("rcstart", rcStart)
|
|
||||||
.addQueryParameter("rcnamespace", FILE_NAMESPACE)
|
|
||||||
.addQueryParameter("rcprop", "title|ids")
|
|
||||||
.addQueryParameter("rctype", "new|log")
|
|
||||||
.addQueryParameter("rctoponly", "1");
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
MwQueryResponse mwQueryPage = gson.fromJson(json, MwQueryResponse.class);
|
|
||||||
return mwQueryPage.query().getRecentChanges();
|
|
||||||
}
|
|
||||||
return new ArrayList<>();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the first revision of the file
|
|
||||||
*
|
|
||||||
* @return Revision object
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public Single<MwQueryPage.Revision> getFirstRevisionOfFile(String filename) {
|
|
||||||
HttpUrl.Builder urlBuilder = HttpUrl
|
|
||||||
.parse(commonsBaseUrl)
|
|
||||||
.newBuilder()
|
|
||||||
.addQueryParameter("action", "query")
|
|
||||||
.addQueryParameter("format", "json")
|
|
||||||
.addQueryParameter("formatversion", "2")
|
|
||||||
.addQueryParameter("prop", "revisions")
|
|
||||||
.addQueryParameter("rvprop", "timestamp|ids|user")
|
|
||||||
.addQueryParameter("titles", filename)
|
|
||||||
.addQueryParameter("rvdir", "newer")
|
|
||||||
.addQueryParameter("rvlimit", "1");
|
|
||||||
|
|
||||||
Request request = new Request.Builder()
|
|
||||||
.url(urlBuilder.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return Single.fromCallable(() -> {
|
|
||||||
Response response = okHttpClient.newCall(request).execute();
|
|
||||||
if (response.body() != null && response.isSuccessful()) {
|
|
||||||
String json = response.body().string();
|
|
||||||
MwQueryResponse mwQueryPage = gson.fromJson(json, MwQueryResponse.class);
|
|
||||||
return mwQueryPage.query().firstPage().revisions().get(0);
|
|
||||||
}
|
|
||||||
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,8 @@ 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) {
|
|
||||||
if (locationManager.isLocationPermissionGranted(requireContext())) {
|
|
||||||
locationManager.registerLocationManager(getActivity());
|
|
||||||
} 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 {
|
locationManager.registerLocationManager(getActivity());
|
||||||
// No explanation needed, we can request the permission.
|
|
||||||
requestLocationPermissions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
locationManager.registerLocationManager(getActivity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 +626,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 +640,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 +659,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
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
||||||
@Override
|
@Override
|
||||||
public void requestLocationPermissions(LocationServiceManager locationServiceManager) {
|
public void requestLocationPermissions(LocationServiceManager locationServiceManager) {
|
||||||
if (!getActivity().isFinishing()) {
|
if (!getActivity().isFinishing()) {
|
||||||
locationServiceManager.requestPermissions(getActivity());
|
locationServiceManager.requestPermissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,20 @@ import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.drawerlayout.widget.DrawerLayout;
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import com.facebook.drawee.view.SimpleDraweeView;
|
import com.facebook.drawee.view.SimpleDraweeView;
|
||||||
import com.google.android.material.navigation.NavigationView;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
import com.viewpagerindicator.CirclePageIndicator;
|
import com.viewpagerindicator.CirclePageIndicator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.R;
|
import fr.free.nrw.commons.R;
|
||||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||||
|
|
@ -29,8 +36,6 @@ import fr.free.nrw.commons.utils.ViewUtil;
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
import java.util.ArrayList;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class ReviewActivity extends AuthenticatedActivity {
|
public class ReviewActivity extends AuthenticatedActivity {
|
||||||
|
|
||||||
|
|
@ -139,7 +144,11 @@ public class ReviewActivity extends AuthenticatedActivity {
|
||||||
compositeDisposable.add(reviewHelper.getRandomMedia()
|
compositeDisposable.add(reviewHelper.getRandomMedia()
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(this::updateImage));
|
.subscribe(media -> {
|
||||||
|
if (media != null) {
|
||||||
|
updateImage(media);
|
||||||
|
}
|
||||||
|
}));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,63 @@
|
||||||
package fr.free.nrw.commons.review;
|
package fr.free.nrw.commons.review;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
import org.wikipedia.dataclient.mwapi.MwQueryPage;
|
||||||
import org.wikipedia.dataclient.mwapi.RecentChange;
|
import org.wikipedia.dataclient.mwapi.RecentChange;
|
||||||
|
import org.wikipedia.util.DateUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Date;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.core.util.Pair;
|
|
||||||
import fr.free.nrw.commons.Media;
|
import fr.free.nrw.commons.Media;
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||||
|
import io.reactivex.Observable;
|
||||||
import io.reactivex.Single;
|
import io.reactivex.Single;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class ReviewHelper {
|
public class ReviewHelper {
|
||||||
private static final int MAX_RANDOM_TRIES = 5;
|
|
||||||
|
|
||||||
private static final String[] imageExtensions = new String[]{".jpg", ".jpeg", ".png"};
|
private static final String[] imageExtensions = new String[]{".jpg", ".jpeg", ".png"};
|
||||||
|
|
||||||
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
private final OkHttpJsonApiClient okHttpJsonApiClient;
|
||||||
private final MediaWikiApi mediaWikiApi;
|
private final MediaWikiApi mediaWikiApi;
|
||||||
|
private final ReviewInterface reviewInterface;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient, MediaWikiApi mediaWikiApi) {
|
public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient,
|
||||||
|
MediaWikiApi mediaWikiApi,
|
||||||
|
ReviewInterface reviewInterface) {
|
||||||
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
this.okHttpJsonApiClient = okHttpJsonApiClient;
|
||||||
this.mediaWikiApi = mediaWikiApi;
|
this.mediaWikiApi = mediaWikiApi;
|
||||||
|
this.reviewInterface = reviewInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
Single<Media> getRandomMedia() {
|
/**
|
||||||
return getRandomFileChange()
|
* Fetches recent changes from MediaWiki API
|
||||||
.flatMap(fileName -> okHttpJsonApiClient.getMedia(fileName, false));
|
* Calls the API to get 10 changes in the last 1 hour
|
||||||
|
* Earlier we were getting changes for the last 30 days but as the API returns just 10 results
|
||||||
|
* its best to fetch for just last 1 hour.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Observable<RecentChange> getRecentChanges() {
|
||||||
|
final int RANDOM_SECONDS = 60 * 60;
|
||||||
|
Random r = new Random();
|
||||||
|
Date now = new Date();
|
||||||
|
Date startDate = new Date(now.getTime() - r.nextInt(RANDOM_SECONDS) * 1000L);
|
||||||
|
|
||||||
|
String rcStart = DateUtil.iso8601DateFormat(startDate);
|
||||||
|
return reviewInterface.getRecentChanges(rcStart)
|
||||||
|
.map(mwQueryResponse -> mwQueryResponse.query().getRecentChanges())
|
||||||
|
.map(recentChanges -> {
|
||||||
|
//Collections.shuffle(recentChanges);
|
||||||
|
return recentChanges;
|
||||||
|
})
|
||||||
|
.flatMapIterable(changes -> changes)
|
||||||
|
.filter(recentChange -> isChangeReviewable(recentChange));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -43,57 +67,62 @@ public class ReviewHelper {
|
||||||
* - Checks if the file is nominated for deletion
|
* - Checks if the file is nominated for deletion
|
||||||
* - Retries upto 5 times for getting a file which is not nominated for deletion
|
* - Retries upto 5 times for getting a file which is not nominated for deletion
|
||||||
*
|
*
|
||||||
|
* @return Random file change
|
||||||
|
*/
|
||||||
|
public Single<Media> getRandomMedia() {
|
||||||
|
return getRecentChanges()
|
||||||
|
.flatMapSingle(change -> getRandomMediaFromRecentChange(change))
|
||||||
|
.onExceptionResumeNext(Observable.just(new Media("")))
|
||||||
|
.filter(media -> !StringUtils.isBlank(media.getFilename()))
|
||||||
|
.firstOrError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a proper Media object if the file is not already nominated for deletion
|
||||||
|
* Else it returns an empty Media object
|
||||||
|
* @param recentChange
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Single<String> getRandomFileChange() {
|
private Single<Media> getRandomMediaFromRecentChange(RecentChange recentChange) {
|
||||||
return okHttpJsonApiClient.getRecentFileChanges()
|
return Single.just(recentChange)
|
||||||
.map(this::findImageInRecentChanges)
|
.flatMap(change -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + change.getTitle()))
|
||||||
.flatMap(title -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + title)
|
.flatMap(isDeleted -> {
|
||||||
.map(pageExists -> new Pair<>(title, pageExists)))
|
if (isDeleted) {
|
||||||
.map((Pair<String, Boolean> pair) -> {
|
return Single.just(new Media(""));
|
||||||
if (!pair.second) {
|
|
||||||
return pair.first;
|
|
||||||
}
|
}
|
||||||
throw new Exception("Already nominated for deletion");
|
return okHttpJsonApiClient.getMedia(recentChange.getTitle(), false);
|
||||||
}).retry(MAX_RANDOM_TRIES);
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Single<MwQueryPage.Revision> getFirstRevisionOfFile(String fileName) {
|
/**
|
||||||
return okHttpJsonApiClient.getFirstRevisionOfFile(fileName);
|
* Gets the first revision of the file from filename
|
||||||
|
* @param filename
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Observable<MwQueryPage.Revision> getFirstRevisionOfFile(String filename) {
|
||||||
|
return reviewInterface.getFirstRevisionOfFile(filename)
|
||||||
|
.map(response -> response.query().firstPage().revisions().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
/**
|
||||||
private String findImageInRecentChanges(List<RecentChange> recentChanges) {
|
* Checks if the change is reviewable or not.
|
||||||
String imageTitle;
|
* - checks the type and revisionId of the change
|
||||||
Random r = new Random();
|
* - checks supported image extensions
|
||||||
int count = recentChanges.size();
|
* @param recentChange
|
||||||
// Build a range array
|
* @return
|
||||||
int[] randomIndexes = new int[count];
|
*/
|
||||||
for (int i = 0; i < count; i++) {
|
private boolean isChangeReviewable(RecentChange recentChange) {
|
||||||
randomIndexes[i] = i;
|
if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// Then shuffle it
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
int swapIndex = r.nextInt(count);
|
|
||||||
int temp = randomIndexes[i];
|
|
||||||
randomIndexes[i] = randomIndexes[swapIndex];
|
|
||||||
randomIndexes[swapIndex] = temp;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
int randomIndex = randomIndexes[i];
|
|
||||||
RecentChange recentChange = recentChanges.get(randomIndex);
|
|
||||||
if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) {
|
|
||||||
// For log entries, we only want ones where old_revid is zero, indicating a new file
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
imageTitle = recentChange.getTitle();
|
|
||||||
|
|
||||||
for (String imageExtension : imageExtensions) {
|
for (String extension : imageExtensions) {
|
||||||
if (imageTitle.toLowerCase().endsWith(imageExtension)) {
|
if (recentChange.getTitle().endsWith(extension)) {
|
||||||
return imageTitle;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package fr.free.nrw.commons.review;
|
||||||
|
|
||||||
|
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
|
||||||
|
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface class for peer review calls
|
||||||
|
*/
|
||||||
|
public interface ReviewInterface {
|
||||||
|
@GET("w/api.php?action=query&format=json&formatversion=2&list=recentchanges&rcprop=title|ids&rctype=new|log&rctoponly=1&rcnamespace=6")
|
||||||
|
Observable<MwQueryResponse> getRecentChanges(@Query("rcstart") String rcStart);
|
||||||
|
|
||||||
|
@GET("w/api.php?action=query&format=json&formatversion=2&prop=revisions&rvprop=timestamp|ids|user&rvdir=newer&rvlimit=1")
|
||||||
|
Observable<MwQueryResponse> getFirstRevisionOfFile(@Query("titles") String titles);
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
* Azouz.anis
|
* Azouz.anis
|
||||||
* ButterflyOfFire
|
* ButterflyOfFire
|
||||||
* Claw eg
|
* Claw eg
|
||||||
|
* Kassem7899
|
||||||
* Meno25
|
* Meno25
|
||||||
* Mido
|
* Mido
|
||||||
* Monrokhoury
|
* Monrokhoury
|
||||||
|
|
@ -22,6 +23,7 @@
|
||||||
<string name="preference_category_appearance">المظهر</string>
|
<string name="preference_category_appearance">المظهر</string>
|
||||||
<string name="preference_category_general">عام</string>
|
<string name="preference_category_general">عام</string>
|
||||||
<string name="preference_category_feedback">التعليقات</string>
|
<string name="preference_category_feedback">التعليقات</string>
|
||||||
|
<string name="preference_category_privacy">الخصوصية</string>
|
||||||
<string name="preference_category_location">الموقع</string>
|
<string name="preference_category_location">الموقع</string>
|
||||||
<string name="app_name">كومنز</string>
|
<string name="app_name">كومنز</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -516,6 +518,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">أمثلة على صور لعدم رفعها</string>
|
<string name="welcome_dont_upload_content_description">أمثلة على صور لعدم رفعها</string>
|
||||||
<string name="skip_image">تخطي هذه الصورة</string>
|
<string name="skip_image">تخطي هذه الصورة</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">التنزيل فشل!!. لا يمكننا تنزيل الملف دون إذن تخزين خارجي.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">التنزيل فشل!!. لا يمكننا تنزيل الملف دون إذن تخزين خارجي.</string>
|
||||||
|
<string name="manage_exif_tags">إدارة وسوم EXIF</string>
|
||||||
|
<string name="manage_exif_tags_summary">حدد أية وسوم EXIF لتحتفظ بها في المرفوعات</string>
|
||||||
|
<string name="exif_tag_name_author">المؤلف</string>
|
||||||
|
<string name="exif_tag_name_copyright">حقوق نشر</string>
|
||||||
|
<string name="exif_tag_name_location">الموقع</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">طراز الكاميرا</string>
|
||||||
|
<string name="exif_tag_name_lensModel">طراز العدسة</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">الأرقام التسلسلية</string>
|
||||||
|
<string name="exif_tag_name_software">برمجية</string>
|
||||||
<string name="share_text">ارفع الصور لويكيميديا كومنز على هاتفك قم بتنزيل تطبيق كومنز: %1$s</string>
|
<string name="share_text">ارفع الصور لويكيميديا كومنز على هاتفك قم بتنزيل تطبيق كومنز: %1$s</string>
|
||||||
<string name="share_via">مشاركة التطبيق عبر...</string>
|
<string name="share_via">مشاركة التطبيق عبر...</string>
|
||||||
<string name="image_info">معلومات الصورة</string>
|
<string name="image_info">معلومات الصورة</string>
|
||||||
|
|
|
||||||
|
|
@ -322,4 +322,5 @@
|
||||||
<string name="yes_submit">হ্যাঁ, জমা দিন</string>
|
<string name="yes_submit">হ্যাঁ, জমা দিন</string>
|
||||||
<string name="no_go_back">না, ফিরে যান</string>
|
<string name="no_go_back">না, ফিরে যান</string>
|
||||||
<string name="please_wait">অনুগ্রহ করে অপেক্ষা করুন...</string>
|
<string name="please_wait">অনুগ্রহ করে অপেক্ষা করুন...</string>
|
||||||
|
<string name="exif_tag_name_location">অবস্থান</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
* FarsiNevis
|
* FarsiNevis
|
||||||
* Fatemi127
|
* Fatemi127
|
||||||
* Freshman404
|
* Freshman404
|
||||||
|
* Ladsgroup
|
||||||
|
* Mardetanha
|
||||||
* Mehdi
|
* Mehdi
|
||||||
* Mjbmr
|
* Mjbmr
|
||||||
* Omidh
|
* Omidh
|
||||||
|
|
@ -18,6 +20,7 @@
|
||||||
* Vahedi
|
* Vahedi
|
||||||
* Yoosef Pooranvary
|
* Yoosef Pooranvary
|
||||||
* جواد
|
* جواد
|
||||||
|
* درفش کاویانی
|
||||||
-->
|
-->
|
||||||
<resources>
|
<resources>
|
||||||
<string name="title_activity_explore">کاوش</string>
|
<string name="title_activity_explore">کاوش</string>
|
||||||
|
|
@ -25,6 +28,7 @@
|
||||||
<string name="preference_category_appearance">نمایش صفحه</string>
|
<string name="preference_category_appearance">نمایش صفحه</string>
|
||||||
<string name="preference_category_general">عمومی</string>
|
<string name="preference_category_general">عمومی</string>
|
||||||
<string name="preference_category_feedback">بازخورد</string>
|
<string name="preference_category_feedback">بازخورد</string>
|
||||||
|
<string name="preference_category_privacy">حریم خصوصی</string>
|
||||||
<string name="preference_category_location">مکان</string>
|
<string name="preference_category_location">مکان</string>
|
||||||
<string name="app_name">ویکیانبار</string>
|
<string name="app_name">ویکیانبار</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -300,9 +304,12 @@
|
||||||
<string name="no_internet">اینترنت در دسترس نیست</string>
|
<string name="no_internet">اینترنت در دسترس نیست</string>
|
||||||
<string name="internet_established">اینترنت در دسترس است</string>
|
<string name="internet_established">اینترنت در دسترس است</string>
|
||||||
<string name="error_notifications">خطا در آوردن اطلاعیه</string>
|
<string name="error_notifications">خطا در آوردن اطلاعیه</string>
|
||||||
|
<string name="error_review">در تلاش برای بدستآوردن تصویر جهت بازبینی خطایی پیش آمد. لطفا برای تلاش مجدد بر روی ریفرش کلیک کنید.</string>
|
||||||
|
<string name="error_review_categories">دریافت ردههای تصویر برای بازبینی به مشکل برخورد کرد. لطفا برای تلاش مجدد صفحه را رفرش کنید.</string>
|
||||||
<string name="no_notifications">هشداری پیدا نشد</string>
|
<string name="no_notifications">هشداری پیدا نشد</string>
|
||||||
<string name="about_translate"><u>ترجمه</u></string>
|
<string name="about_translate"><u>ترجمه</u></string>
|
||||||
<string name="about_translate_title">زبانها</string>
|
<string name="about_translate_title">زبانها</string>
|
||||||
|
<string name="about_translate_message">لطفا زبانی که مایلید در آنها ترجمههای خود را ثبت کنید انتخاب کنید.</string>
|
||||||
<string name="about_translate_proceed">ادامه</string>
|
<string name="about_translate_proceed">ادامه</string>
|
||||||
<string name="about_translate_cancel">لغو</string>
|
<string name="about_translate_cancel">لغو</string>
|
||||||
<string name="retry">سعى دوباره</string>
|
<string name="retry">سعى دوباره</string>
|
||||||
|
|
@ -317,11 +324,15 @@
|
||||||
<string name="search_commons">جستجوی ویکیانبار</string>
|
<string name="search_commons">جستجوی ویکیانبار</string>
|
||||||
<string name="title_activity_search">جستجو</string>
|
<string name="title_activity_search">جستجو</string>
|
||||||
<string name="search_recent_header">جستجوهای اخیر:</string>
|
<string name="search_recent_header">جستجوهای اخیر:</string>
|
||||||
|
<string name="provider_searches">کوئریهای که اخیرا جستجو شده</string>
|
||||||
<string name="error_loading_categories">خطا هنگام بار کردن رده ها.</string>
|
<string name="error_loading_categories">خطا هنگام بار کردن رده ها.</string>
|
||||||
|
<string name="error_loading_subcategories">در هنگام بارگیری زیرردهها خطایی رخ داد.</string>
|
||||||
<string name="search_tab_title_media">رسانه</string>
|
<string name="search_tab_title_media">رسانه</string>
|
||||||
<string name="search_tab_title_categories">دسته بندی</string>
|
<string name="search_tab_title_categories">دسته بندی</string>
|
||||||
<string name="explore_tab_title_featured">برگزیده</string>
|
<string name="explore_tab_title_featured">برگزیده</string>
|
||||||
<string name="explore_tab_title_mobile">بارگذاریشده با تلفن همراه</string>
|
<string name="explore_tab_title_mobile">بارگذاریشده با تلفن همراه</string>
|
||||||
|
<string name="successful_wikidata_edit">تصویر با موفقیت به %1$s در ویکیداده افزودهشد.</string>
|
||||||
|
<string name="wikidata_edit_failure">تلاش برای بروزرسانی موجودی ویکیدیتای مرتبط شکست خورد</string>
|
||||||
<string name="menu_set_wallpaper">انتخاب به عنوان پسزمینه</string>
|
<string name="menu_set_wallpaper">انتخاب به عنوان پسزمینه</string>
|
||||||
<string name="wallpaper_set_successfully">تصویر پس زمینه به طور موفقیت آمیز تنظیم شد!</string>
|
<string name="wallpaper_set_successfully">تصویر پس زمینه به طور موفقیت آمیز تنظیم شد!</string>
|
||||||
<string name="quiz">امتحان</string>
|
<string name="quiz">امتحان</string>
|
||||||
|
|
@ -330,6 +341,7 @@
|
||||||
<string name="result">نتیجه</string>
|
<string name="result">نتیجه</string>
|
||||||
<string name="warning_for_no_answer">یکی از دو گزینه را انتخاب کنید تا به سوال پاسخ دهید</string>
|
<string name="warning_for_no_answer">یکی از دو گزینه را انتخاب کنید تا به سوال پاسخ دهید</string>
|
||||||
<string name="user_not_logged_in">جلسه ورود به سیستم منقضی شد، لطفا دوباره وارد سیستم شوید.</string>
|
<string name="user_not_logged_in">جلسه ورود به سیستم منقضی شد، لطفا دوباره وارد سیستم شوید.</string>
|
||||||
|
<string name="quiz_result_share_message">کویز خود را با دوستان خود به اشتراک بگذارید.</string>
|
||||||
<string name="continue_message">ادامه</string>
|
<string name="continue_message">ادامه</string>
|
||||||
<string name="correct">جواب درست</string>
|
<string name="correct">جواب درست</string>
|
||||||
<string name="wrong">جواب نادرست</string>
|
<string name="wrong">جواب نادرست</string>
|
||||||
|
|
@ -348,6 +360,7 @@
|
||||||
<string name="statistics">آمارها</string>
|
<string name="statistics">آمارها</string>
|
||||||
<string name="statistics_thanks">تشکر دریافتشد</string>
|
<string name="statistics_thanks">تشکر دریافتشد</string>
|
||||||
<string name="statistics_featured">تصاویر برگزیده</string>
|
<string name="statistics_featured">تصاویر برگزیده</string>
|
||||||
|
<string name="statistics_wikidata_edits">تصاویر بر اساس «مکانهای اطراف»</string>
|
||||||
<string name="level">سطح</string>
|
<string name="level">سطح</string>
|
||||||
<string name="images_uploaded">تصاویر بارگذاری شده</string>
|
<string name="images_uploaded">تصاویر بارگذاری شده</string>
|
||||||
<string name="image_reverts">تصاویر واگردانی نشده</string>
|
<string name="image_reverts">تصاویر واگردانی نشده</string>
|
||||||
|
|
@ -361,6 +374,9 @@
|
||||||
<string name="contributions_fragment">مشارکتها</string>
|
<string name="contributions_fragment">مشارکتها</string>
|
||||||
<string name="nearby_fragment">در نزدیکی</string>
|
<string name="nearby_fragment">در نزدیکی</string>
|
||||||
<string name="notifications">آگاهسازیها</string>
|
<string name="notifications">آگاهسازیها</string>
|
||||||
|
<string name="archived_notifications">اعلانها (بایگانیشده)</string>
|
||||||
|
<string name="display_nearby_notification">نمایش اعلان اطراف</string>
|
||||||
|
<string name="no_close_nearby">هیچ مکان نزدیکی به شما یافته نشد</string>
|
||||||
<string name="list_sheet">فهرست</string>
|
<string name="list_sheet">فهرست</string>
|
||||||
<string name="storage_permission">اجازه ذخیره</string>
|
<string name="storage_permission">اجازه ذخیره</string>
|
||||||
<string name="step_count">گام %1$d از %2$d</string>
|
<string name="step_count">گام %1$d از %2$d</string>
|
||||||
|
|
@ -396,25 +412,55 @@
|
||||||
<string name="search_this_area">جستجوی این محدوده</string>
|
<string name="search_this_area">جستجوی این محدوده</string>
|
||||||
<string name="nearby_card_permission_title">درخواست اجازه</string>
|
<string name="nearby_card_permission_title">درخواست اجازه</string>
|
||||||
<string name="never_ask_again">این را دیگر نپرس</string>
|
<string name="never_ask_again">این را دیگر نپرس</string>
|
||||||
|
<string name="display_campaigns">کمپینهای نمایش</string>
|
||||||
<string name="error_processing_image">هنگام پردازش این تصویر خطایی رخ داد. لطفا دوباره سعی کنید!</string>
|
<string name="error_processing_image">هنگام پردازش این تصویر خطایی رخ داد. لطفا دوباره سعی کنید!</string>
|
||||||
|
<string name="getting_edit_token">دریافت توکن برای ویرایش</string>
|
||||||
|
<string name="check_category_edit_summary">درخواست بررسی رده</string>
|
||||||
|
<string name="check_category_success_title">بررسی رده درخواست شده</string>
|
||||||
|
<string name="check_category_failure_title">درخواست بررسی رده کار نکرد</string>
|
||||||
|
<string name="nominate_for_deletion_edit_file_page">افزودن پیام حذفشده به فایل</string>
|
||||||
<string name="nominate_for_deletion_done">انجام شد</string>
|
<string name="nominate_for_deletion_done">انجام شد</string>
|
||||||
|
<string name="nominate_for_deletion_notify_user">به کاربر در صفحه بحثش خبر بده</string>
|
||||||
<string name="notsure">مطمئن نیستم</string>
|
<string name="notsure">مطمئن نیستم</string>
|
||||||
<string name="send_thank_success_title">ارسال تشکر: موفق</string>
|
<string name="send_thank_success_title">ارسال تشکر: موفق</string>
|
||||||
|
<string name="send_thank_failure_message">تلاش برای فرستادن تشکر شکست خورد %1$s</string>
|
||||||
<string name="send_thank_failure_title">ارسال تشکر: ناموفق</string>
|
<string name="send_thank_failure_title">ارسال تشکر: ناموفق</string>
|
||||||
<string name="send_thank_send">ارسال تشکر</string>
|
<string name="send_thank_send">ارسال تشکر</string>
|
||||||
<string name="send_thank_notification_title">ارسال تشکر</string>
|
<string name="send_thank_notification_title">ارسال تشکر</string>
|
||||||
<string name="send_thank_toast">در حال ارسال تشکر برای %1$s</string>
|
<string name="send_thank_toast">در حال ارسال تشکر برای %1$s</string>
|
||||||
<string name="review_category">آیا این به درستی ردهبندی شدهاست؟</string>
|
<string name="review_category">آیا این به درستی ردهبندی شدهاست؟</string>
|
||||||
|
<string name="review_spam">آیا این در محدوده قابل قبول است؟</string>
|
||||||
|
<string name="review_thanks">آیا مایلید که از مشارکت کننده تشکر کنید؟</string>
|
||||||
|
<string name="review_no_category">عجب، این حتی ردهبندی هم نشده!</string>
|
||||||
|
<string name="review_spam_report_question">این از محدوده خارج است زیرا که</string>
|
||||||
|
<string name="review_c_violation_report_question">این فایل ناقض حق تکثیر است به خاطر اینکه</string>
|
||||||
<string name="review_category_no_button_text">خوب به نظر میرسد</string>
|
<string name="review_category_no_button_text">خوب به نظر میرسد</string>
|
||||||
|
<string name="review_spam_yes_button_text">نه، این از محدوده خارج است</string>
|
||||||
<string name="review_spam_no_button_text">خوب به نظر میرسد</string>
|
<string name="review_spam_no_button_text">خوب به نظر میرسد</string>
|
||||||
<string name="review_copyright_no_button_text">خوب به نظر میرسد</string>
|
<string name="review_copyright_no_button_text">خوب به نظر میرسد</string>
|
||||||
<string name="review_thanks_yes_button_text">بله، چرا که نه</string>
|
<string name="review_thanks_yes_button_text">بله، چرا که نه</string>
|
||||||
<string name="review_thanks_no_button_text">تصویر بعدی</string>
|
<string name="review_thanks_no_button_text">تصویر بعدی</string>
|
||||||
|
<string name="no_image">تصاویر استاده نشده</string>
|
||||||
<string name="no_image_reverted">تصویر برگردانده نشد</string>
|
<string name="no_image_reverted">تصویر برگردانده نشد</string>
|
||||||
<string name="no_image_uploaded">هیچ تصویری بارگذاری نشد</string>
|
<string name="no_image_uploaded">هیچ تصویری بارگذاری نشد</string>
|
||||||
|
<string name="no_notification">شما هیچ اعلان خواندهنشدهای ندارید</string>
|
||||||
|
<string name="no_archived_notification">شما هیچ پیغام بایگانی شدهای ندارید</string>
|
||||||
<string name="menu_option_archived">نمایش بایگانیشده</string>
|
<string name="menu_option_archived">نمایش بایگانیشده</string>
|
||||||
<string name="menu_option_unread">مشاهده خوانده نشده ها</string>
|
<string name="menu_option_unread">مشاهده خوانده نشده ها</string>
|
||||||
<string name="image_chooser_title">انتخاب تصویر برای بارگذاری</string>
|
<string name="image_chooser_title">انتخاب تصویر برای بارگذاری</string>
|
||||||
<string name="please_wait">لطفاً صبر کنید...</string>
|
<string name="please_wait">لطفاً صبر کنید...</string>
|
||||||
|
<string name="previous_image_title_description">از عنوان/توضیحات پیشین استفاده کنید</string>
|
||||||
|
<string name="welcome_dont_upload_content_description">نمونه تصاویری که برای بازگذاری مناسب نیستند</string>
|
||||||
|
<string name="skip_image">از این تصویر صرف نظر کن</string>
|
||||||
|
<string name="manage_exif_tags">مدیریت تگهای EXIF</string>
|
||||||
|
<string name="manage_exif_tags_summary">تگهای موردنظر خود در EXIF را برای آپلود انتخاب کنید</string>
|
||||||
|
<string name="exif_tag_name_author">پدیدآور</string>
|
||||||
|
<string name="exif_tag_name_copyright">حق تکثیر</string>
|
||||||
|
<string name="exif_tag_name_location">مکان</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">مدل دوربین</string>
|
||||||
|
<string name="exif_tag_name_lensModel">مدل لنز</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">شماره سریال</string>
|
||||||
|
<string name="exif_tag_name_software">نرمافزار</string>
|
||||||
|
<string name="share_via">اشتراک از طریق...</string>
|
||||||
<string name="image_info">اطلاعات عکس</string>
|
<string name="image_info">اطلاعات عکس</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
<string name="preference_category_appearance">Apparence</string>
|
<string name="preference_category_appearance">Apparence</string>
|
||||||
<string name="preference_category_general">Général</string>
|
<string name="preference_category_general">Général</string>
|
||||||
<string name="preference_category_feedback">Donner son avis</string>
|
<string name="preference_category_feedback">Donner son avis</string>
|
||||||
|
<string name="preference_category_privacy">Confidentialité</string>
|
||||||
<string name="preference_category_location">Emplacement</string>
|
<string name="preference_category_location">Emplacement</string>
|
||||||
<string name="app_name">Commons</string>
|
<string name="app_name">Commons</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -527,6 +528,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Exemples d\'images à ne pas téléverser</string>
|
<string name="welcome_dont_upload_content_description">Exemples d\'images à ne pas téléverser</string>
|
||||||
<string name="skip_image">SAUTER CETTE IMAGE</string>
|
<string name="skip_image">SAUTER CETTE IMAGE</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Échec du téléchargement ! Nous ne pouvons pas télécharger le fichier sans droit de stockage externe.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Échec du téléchargement ! Nous ne pouvons pas télécharger le fichier sans droit de stockage externe.</string>
|
||||||
|
<string name="manage_exif_tags">Gérer les balises EXIF</string>
|
||||||
|
<string name="manage_exif_tags_summary">Sélectionner quelles balises EXIF à conserver dans les téléchargements</string>
|
||||||
|
<string name="exif_tag_name_author">Auteur</string>
|
||||||
|
<string name="exif_tag_name_copyright">Droits d’auteur</string>
|
||||||
|
<string name="exif_tag_name_location">Emplacement</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Modèle d’appareil photo</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Modèle de lentille</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Numéros de série</string>
|
||||||
|
<string name="exif_tag_name_software">Logiciel</string>
|
||||||
<string name="share_text">Téléverser des photos vers Wikimédia Communs, sur votre téléphone Téléchargez l’application Communs : %1$s</string>
|
<string name="share_text">Téléverser des photos vers Wikimédia Communs, sur votre téléphone Téléchargez l’application Communs : %1$s</string>
|
||||||
<string name="share_via">Partager l’application via…</string>
|
<string name="share_via">Partager l’application via…</string>
|
||||||
<string name="image_info">Informations de l’image</string>
|
<string name="image_info">Informations de l’image</string>
|
||||||
|
|
|
||||||
|
|
@ -469,6 +469,7 @@
|
||||||
<string name="previous_image_title_description">Copia titolo/descrizione precedente</string>
|
<string name="previous_image_title_description">Copia titolo/descrizione precedente</string>
|
||||||
<string name="previous_button_tooltip_message">Clicca per riusare il titolo e la descrizione dell\'immagine precedente e adattarli all\'immagine attuale.</string>
|
<string name="previous_button_tooltip_message">Clicca per riusare il titolo e la descrizione dell\'immagine precedente e adattarli all\'immagine attuale.</string>
|
||||||
<string name="skip_image">SALTA QUESTA IMMAGINE</string>
|
<string name="skip_image">SALTA QUESTA IMMAGINE</string>
|
||||||
|
<string name="exif_tag_name_author">Autore</string>
|
||||||
<string name="share_via">Condividi applicazione tramite...</string>
|
<string name="share_via">Condividi applicazione tramite...</string>
|
||||||
<string name="image_info">Informazioni sull\'immagine</string>
|
<string name="image_info">Informazioni sull\'immagine</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
<string name="preference_category_appearance">보이기</string>
|
<string name="preference_category_appearance">보이기</string>
|
||||||
<string name="preference_category_general">일반</string>
|
<string name="preference_category_general">일반</string>
|
||||||
<string name="preference_category_feedback">피드백</string>
|
<string name="preference_category_feedback">피드백</string>
|
||||||
|
<string name="preference_category_privacy">개인정보</string>
|
||||||
<string name="preference_category_location">위치</string>
|
<string name="preference_category_location">위치</string>
|
||||||
<string name="app_name">공용</string>
|
<string name="app_name">공용</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -452,6 +453,13 @@
|
||||||
<string name="previous_image_title_description">이전의 제목 및 설명 복사</string>
|
<string name="previous_image_title_description">이전의 제목 및 설명 복사</string>
|
||||||
<string name="skip_image">이 이미지 건너뛰기</string>
|
<string name="skip_image">이 이미지 건너뛰기</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">다운로드를 실패했습니다!! 외장 스토리지 권한 없이 파일을 다운로드할 수 없습니다.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">다운로드를 실패했습니다!! 외장 스토리지 권한 없이 파일을 다운로드할 수 없습니다.</string>
|
||||||
|
<string name="manage_exif_tags">EXIF 태그 관리</string>
|
||||||
|
<string name="exif_tag_name_author">만든이</string>
|
||||||
|
<string name="exif_tag_name_copyright">저작권</string>
|
||||||
|
<string name="exif_tag_name_location">위치</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">카메라 모델</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">일련 번호</string>
|
||||||
|
<string name="exif_tag_name_software">소프트웨어</string>
|
||||||
<string name="share_via">앱 공유...</string>
|
<string name="share_via">앱 공유...</string>
|
||||||
<string name="image_info">이미지 정보</string>
|
<string name="image_info">이미지 정보</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
<string name="preference_category_appearance">Изглед</string>
|
<string name="preference_category_appearance">Изглед</string>
|
||||||
<string name="preference_category_general">Општи</string>
|
<string name="preference_category_general">Општи</string>
|
||||||
<string name="preference_category_feedback">Мислења</string>
|
<string name="preference_category_feedback">Мислења</string>
|
||||||
|
<string name="preference_category_privacy">Лични податоци</string>
|
||||||
<string name="preference_category_location">Место</string>
|
<string name="preference_category_location">Место</string>
|
||||||
<string name="app_name">Ризница</string>
|
<string name="app_name">Ризница</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -507,6 +508,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Примери — Слики што не се за подигање</string>
|
<string name="welcome_dont_upload_content_description">Примери — Слики што не се за подигање</string>
|
||||||
<string name="skip_image">ПРЕСКОКНИ ЈА ПОРАКАВА</string>
|
<string name="skip_image">ПРЕСКОКНИ ЈА ПОРАКАВА</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Преземањето не успеа!!! Не можеме да ја преземеме податотеката без дозвола од надворешен склад.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Преземањето не успеа!!! Не можеме да ја преземеме податотеката без дозвола од надворешен склад.</string>
|
||||||
|
<string name="manage_exif_tags">Раков. со EXIF-ознаки</string>
|
||||||
|
<string name="manage_exif_tags_summary">Изберете кои EXIF-ознаки да се задржат во подигањата</string>
|
||||||
|
<string name="exif_tag_name_author">Автор</string>
|
||||||
|
<string name="exif_tag_name_copyright">Авторски права</string>
|
||||||
|
<string name="exif_tag_name_location">Место</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Модел на камерата</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Модел на објективот</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Сериски броеви</string>
|
||||||
|
<string name="exif_tag_name_software">Програми</string>
|
||||||
<string name="share_text">Подигајте слики на Ризницата од телефон. Преземете го прилогот на Ризницата: %1$s</string>
|
<string name="share_text">Подигајте слики на Ризницата од телефон. Преземете го прилогот на Ризницата: %1$s</string>
|
||||||
<string name="share_via">Сподели преку...</string>
|
<string name="share_via">Сподели преку...</string>
|
||||||
<string name="image_info">Инфо за сликата</string>
|
<string name="image_info">Инфо за сликата</string>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
<string name="preference_category_appearance">Aparência</string>
|
<string name="preference_category_appearance">Aparência</string>
|
||||||
<string name="preference_category_general">Geral</string>
|
<string name="preference_category_general">Geral</string>
|
||||||
<string name="preference_category_feedback">Comentário</string>
|
<string name="preference_category_feedback">Comentário</string>
|
||||||
|
<string name="preference_category_privacy">Privacidade</string>
|
||||||
<string name="preference_category_location">Localização</string>
|
<string name="preference_category_location">Localização</string>
|
||||||
<string name="app_name">Commons</string>
|
<string name="app_name">Commons</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -519,6 +520,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Exemplos de imagens que não devem ser carregadas</string>
|
<string name="welcome_dont_upload_content_description">Exemplos de imagens que não devem ser carregadas</string>
|
||||||
<string name="skip_image">PULAR ESTA IMAGEM</string>
|
<string name="skip_image">PULAR ESTA IMAGEM</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Falha no Download!!. Não podemos fazer o download do arquivo sem permissão de armazenamento externo.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Falha no Download!!. Não podemos fazer o download do arquivo sem permissão de armazenamento externo.</string>
|
||||||
|
<string name="manage_exif_tags">Gerenciar etiquetas EXIF</string>
|
||||||
|
<string name="manage_exif_tags_summary">Selecione quais etiquetas EXIF para manter nos carregados</string>
|
||||||
|
<string name="exif_tag_name_author">Autor</string>
|
||||||
|
<string name="exif_tag_name_copyright">Direitos autorais</string>
|
||||||
|
<string name="exif_tag_name_location">Localização</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Modelo da câmera</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Modelo de lente</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Números de série</string>
|
||||||
|
<string name="exif_tag_name_software">Software</string>
|
||||||
<string name="share_text">Faça o carregamento de fotos para o Wikimedia Commons no seu telefone ou baixe o aplicativo Commons: %1$s</string>
|
<string name="share_text">Faça o carregamento de fotos para o Wikimedia Commons no seu telefone ou baixe o aplicativo Commons: %1$s</string>
|
||||||
<string name="share_via">Compartilhar aplicativo via...</string>
|
<string name="share_via">Compartilhar aplicativo via...</string>
|
||||||
<string name="image_info">Informação da imagem</string>
|
<string name="image_info">Informação da imagem</string>
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
<string name="preference_category_appearance">Внешний вид</string>
|
<string name="preference_category_appearance">Внешний вид</string>
|
||||||
<string name="preference_category_general">Общие</string>
|
<string name="preference_category_general">Общие</string>
|
||||||
<string name="preference_category_feedback">Обратная связь</string>
|
<string name="preference_category_feedback">Обратная связь</string>
|
||||||
|
<string name="preference_category_privacy">Конфиденциальность</string>
|
||||||
<string name="preference_category_location">Местоположение</string>
|
<string name="preference_category_location">Местоположение</string>
|
||||||
<string name="app_name">Викисклад</string>
|
<string name="app_name">Викисклад</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -526,6 +527,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Примеры изображений, которые не следует загружать на Викисклад</string>
|
<string name="welcome_dont_upload_content_description">Примеры изображений, которые не следует загружать на Викисклад</string>
|
||||||
<string name="skip_image">ПРОПУСТИТЬ ЭТО ИЗОБРАЖЕНИЕ</string>
|
<string name="skip_image">ПРОПУСТИТЬ ЭТО ИЗОБРАЖЕНИЕ</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Скачивание файла не удалось! Это невозможно выполнить без предоставленного разрешения по использованию внешнего носителя.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Скачивание файла не удалось! Это невозможно выполнить без предоставленного разрешения по использованию внешнего носителя.</string>
|
||||||
|
<string name="manage_exif_tags">Работа с EXIF-тегами</string>
|
||||||
|
<string name="manage_exif_tags_summary">Укажите, какие EXIF-теги следует сохранить при загрузке файлов</string>
|
||||||
|
<string name="exif_tag_name_author">Автор</string>
|
||||||
|
<string name="exif_tag_name_copyright">Авторские права</string>
|
||||||
|
<string name="exif_tag_name_location">Местоположение</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Модель камеры</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Модель объектива</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Серийный номер</string>
|
||||||
|
<string name="exif_tag_name_software">Программное обеспечение</string>
|
||||||
<string name="share_text">Чтобы загружать фото на Викисклад (Wikimedia Commons), скачайте одноимённое приложение «Викисклад» (Commons): %1$s</string>
|
<string name="share_text">Чтобы загружать фото на Викисклад (Wikimedia Commons), скачайте одноимённое приложение «Викисклад» (Commons): %1$s</string>
|
||||||
<string name="share_via">Поделиться приложением с помощью...</string>
|
<string name="share_via">Поделиться приложением с помощью...</string>
|
||||||
<string name="image_info">Информация об изображении</string>
|
<string name="image_info">Информация об изображении</string>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<string name="preference_category_appearance">Utseende</string>
|
<string name="preference_category_appearance">Utseende</string>
|
||||||
<string name="preference_category_general">Allmänt</string>
|
<string name="preference_category_general">Allmänt</string>
|
||||||
<string name="preference_category_feedback">Återkoppling</string>
|
<string name="preference_category_feedback">Återkoppling</string>
|
||||||
|
<string name="preference_category_privacy">Integritet</string>
|
||||||
<string name="preference_category_location">Plats</string>
|
<string name="preference_category_location">Plats</string>
|
||||||
<string name="app_name">Commons</string>
|
<string name="app_name">Commons</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -512,6 +513,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Exempel på bilder att inte ladda upp</string>
|
<string name="welcome_dont_upload_content_description">Exempel på bilder att inte ladda upp</string>
|
||||||
<string name="skip_image">HOPPA ÖVER DENNA BILD</string>
|
<string name="skip_image">HOPPA ÖVER DENNA BILD</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Nedladdning misslyckades!! Vi kan inte ladda ned filen utan behörighet för extern lagring.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Nedladdning misslyckades!! Vi kan inte ladda ned filen utan behörighet för extern lagring.</string>
|
||||||
|
<string name="manage_exif_tags">Hantera EXIF-taggar</string>
|
||||||
|
<string name="manage_exif_tags_summary">Välj vilka EXIF-taggar att behålla i uppladdningar</string>
|
||||||
|
<string name="exif_tag_name_author">Skapare</string>
|
||||||
|
<string name="exif_tag_name_copyright">Upphovsrätt</string>
|
||||||
|
<string name="exif_tag_name_location">Plats</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Kameramodell</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Linsmodell</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Serienummer</string>
|
||||||
|
<string name="exif_tag_name_software">Programvara</string>
|
||||||
<string name="share_text">Ladda upp foton till Wikimedia Commons på din telefon Ladda ned Commons-appen: %1$s</string>
|
<string name="share_text">Ladda upp foton till Wikimedia Commons på din telefon Ladda ned Commons-appen: %1$s</string>
|
||||||
<string name="share_via">Dela appen via...</string>
|
<string name="share_via">Dela appen via...</string>
|
||||||
<string name="image_info">Bildinfo</string>
|
<string name="image_info">Bildinfo</string>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
<string name="preference_category_appearance">Зовнішній вигляд</string>
|
<string name="preference_category_appearance">Зовнішній вигляд</string>
|
||||||
<string name="preference_category_general">Загальні</string>
|
<string name="preference_category_general">Загальні</string>
|
||||||
<string name="preference_category_feedback">Зворотний зв\'язок</string>
|
<string name="preference_category_feedback">Зворотний зв\'язок</string>
|
||||||
|
<string name="preference_category_privacy">Конфіденційність</string>
|
||||||
<string name="preference_category_location">Розташування</string>
|
<string name="preference_category_location">Розташування</string>
|
||||||
<string name="app_name">Вікісховище</string>
|
<string name="app_name">Вікісховище</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -526,6 +527,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">Приклади зображень, які не слід завантажувати</string>
|
<string name="welcome_dont_upload_content_description">Приклади зображень, які не слід завантажувати</string>
|
||||||
<string name="skip_image">ПРОПУСТИТИ ЦЕ ЗОБРАЖЕННЯ</string>
|
<string name="skip_image">ПРОПУСТИТИ ЦЕ ЗОБРАЖЕННЯ</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Завантаження не вдалося. Ми не змогли завантажити файл без доступу до зовнішнього носія.</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">Завантаження не вдалося. Ми не змогли завантажити файл без доступу до зовнішнього носія.</string>
|
||||||
|
<string name="manage_exif_tags">Робота з EXIF-тегами</string>
|
||||||
|
<string name="manage_exif_tags_summary">Вкажіть, які EXIF-теги мають бути збережені при завантаженні файлів</string>
|
||||||
|
<string name="exif_tag_name_author">Автор</string>
|
||||||
|
<string name="exif_tag_name_copyright">Авторські права</string>
|
||||||
|
<string name="exif_tag_name_location">Місцезнаходження</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">Модель камери</string>
|
||||||
|
<string name="exif_tag_name_lensModel">Модель об\'єктиву</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">Серійний номер</string>
|
||||||
|
<string name="exif_tag_name_software">Програмне забезпечення</string>
|
||||||
<string name="share_text">Вивантажуйте фото у Вікісховище зі свого телефона. Завантажте застосунок: %1$s</string>
|
<string name="share_text">Вивантажуйте фото у Вікісховище зі свого телефона. Завантажте застосунок: %1$s</string>
|
||||||
<string name="share_via">Поділитися програмкою через…</string>
|
<string name="share_via">Поділитися програмкою через…</string>
|
||||||
<string name="image_info">Інформація про зображення</string>
|
<string name="image_info">Інформація про зображення</string>
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<string name="preference_category_appearance">外觀</string>
|
<string name="preference_category_appearance">外觀</string>
|
||||||
<string name="preference_category_general">一般</string>
|
<string name="preference_category_general">一般</string>
|
||||||
<string name="preference_category_feedback">意見回饋</string>
|
<string name="preference_category_feedback">意見回饋</string>
|
||||||
|
<string name="preference_category_privacy">隱私</string>
|
||||||
<string name="preference_category_location">位置</string>
|
<string name="preference_category_location">位置</string>
|
||||||
<string name="app_name">維基共享資源</string>
|
<string name="app_name">維基共享資源</string>
|
||||||
<string name="bullet">•</string>
|
<string name="bullet">•</string>
|
||||||
|
|
@ -516,6 +517,15 @@
|
||||||
<string name="welcome_dont_upload_content_description">未上傳範例圖片</string>
|
<string name="welcome_dont_upload_content_description">未上傳範例圖片</string>
|
||||||
<string name="skip_image">忽略此圖片</string>
|
<string name="skip_image">忽略此圖片</string>
|
||||||
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">下載失敗!因為缺少外部存儲裝置權限緣故,我們無法下載檔案。</string>
|
<string name="download_failed_we_cannot_download_the_file_without_storage_permission">下載失敗!因為缺少外部存儲裝置權限緣故,我們無法下載檔案。</string>
|
||||||
|
<string name="manage_exif_tags">管理 EXIF 標籤</string>
|
||||||
|
<string name="manage_exif_tags_summary">選擇要保持上傳的 EXIF 標籤</string>
|
||||||
|
<string name="exif_tag_name_author">作者</string>
|
||||||
|
<string name="exif_tag_name_copyright">版權</string>
|
||||||
|
<string name="exif_tag_name_location">位置</string>
|
||||||
|
<string name="exif_tag_name_cameraModel">相機模型</string>
|
||||||
|
<string name="exif_tag_name_lensModel">透鏡模型</string>
|
||||||
|
<string name="exif_tag_name_serialNumbers">序號</string>
|
||||||
|
<string name="exif_tag_name_software">軟體</string>
|
||||||
<string name="share_text">在您的手機上更新照片到維基共享資源,下載共享資源應用程式:%1$s</string>
|
<string name="share_text">在您的手機上更新照片到維基共享資源,下載共享資源應用程式:%1$s</string>
|
||||||
<string name="share_via">分享應用程式透過…</string>
|
<string name="share_via">分享應用程式透過…</string>
|
||||||
<string name="image_info">圖片資訊</string>
|
<string name="image_info">圖片資訊</string>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@ package fr.free.nrw.commons.review
|
||||||
import fr.free.nrw.commons.Media
|
import fr.free.nrw.commons.Media
|
||||||
import fr.free.nrw.commons.mwapi.MediaWikiApi
|
import fr.free.nrw.commons.mwapi.MediaWikiApi
|
||||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||||
|
import io.reactivex.Observable
|
||||||
import io.reactivex.Single
|
import io.reactivex.Single
|
||||||
|
import junit.framework.Assert.assertNotNull
|
||||||
|
import junit.framework.Assert.assertNull
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
@ -13,6 +16,8 @@ import org.mockito.Mock
|
||||||
import org.mockito.Mockito.*
|
import org.mockito.Mockito.*
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
import org.wikipedia.dataclient.mwapi.MwQueryPage
|
import org.wikipedia.dataclient.mwapi.MwQueryPage
|
||||||
|
import org.wikipedia.dataclient.mwapi.MwQueryResponse
|
||||||
|
import org.wikipedia.dataclient.mwapi.MwQueryResult
|
||||||
import org.wikipedia.dataclient.mwapi.RecentChange
|
import org.wikipedia.dataclient.mwapi.RecentChange
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -20,6 +25,8 @@ import org.wikipedia.dataclient.mwapi.RecentChange
|
||||||
*/
|
*/
|
||||||
class ReviewHelperTest {
|
class ReviewHelperTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
internal var reviewInterface: ReviewInterface? = null
|
||||||
@Mock
|
@Mock
|
||||||
internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null
|
internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null
|
||||||
@Mock
|
@Mock
|
||||||
|
|
@ -35,6 +42,31 @@ class ReviewHelperTest {
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
|
|
||||||
|
val mwQueryPage = mock(MwQueryPage::class.java)
|
||||||
|
val mockRevision = mock(MwQueryPage.Revision::class.java)
|
||||||
|
`when`(mockRevision.user).thenReturn("TestUser")
|
||||||
|
`when`(mwQueryPage.revisions()).thenReturn(listOf(mockRevision))
|
||||||
|
|
||||||
|
val recentChange = getMockRecentChange("test", "File:Test1.jpeg", 0)
|
||||||
|
val recentChange1 = getMockRecentChange("test", "File:Test2.png", 0)
|
||||||
|
val recentChange2 = getMockRecentChange("test", "File:Test3.jpg", 0)
|
||||||
|
val mwQueryResult = mock(MwQueryResult::class.java)
|
||||||
|
`when`(mwQueryResult.recentChanges).thenReturn(listOf(recentChange, recentChange1, recentChange2))
|
||||||
|
`when`(mwQueryResult.firstPage()).thenReturn(mwQueryPage)
|
||||||
|
`when`(mwQueryResult.pages()).thenReturn(listOf(mwQueryPage))
|
||||||
|
val mockResponse = mock(MwQueryResponse::class.java)
|
||||||
|
`when`(mockResponse.query()).thenReturn(mwQueryResult)
|
||||||
|
`when`(reviewInterface?.getRecentChanges(ArgumentMatchers.anyString()))
|
||||||
|
.thenReturn(Observable.just(mockResponse))
|
||||||
|
|
||||||
|
`when`(reviewInterface?.getFirstRevisionOfFile(ArgumentMatchers.anyString()))
|
||||||
|
.thenReturn(Observable.just(mockResponse))
|
||||||
|
|
||||||
|
val media = mock(Media::class.java)
|
||||||
|
`when`(media.filename).thenReturn("File:Test.jpg")
|
||||||
|
`when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean()))
|
||||||
|
.thenReturn(Single.just(media))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -42,40 +74,48 @@ class ReviewHelperTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun getRandomMedia() {
|
fun getRandomMedia() {
|
||||||
val recentChange = getMockRecentChange("test", "File:Test1.jpeg", 0)
|
|
||||||
val recentChange1 = getMockRecentChange("test", "File:Test2.png", 0)
|
|
||||||
val recentChange2 = getMockRecentChange("test", "File:Test3.jpg", 0)
|
|
||||||
`when`(okHttpJsonApiClient?.recentFileChanges)
|
|
||||||
.thenReturn(Single.just(listOf(recentChange, recentChange1, recentChange2)))
|
|
||||||
|
|
||||||
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString()))
|
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Single.just(false))
|
.thenReturn(Single.just(false))
|
||||||
`when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean()))
|
|
||||||
.thenReturn(Single.just(mock(Media::class.java)))
|
|
||||||
|
|
||||||
|
|
||||||
val randomMedia = reviewHelper?.randomMedia?.blockingGet()
|
val randomMedia = reviewHelper?.randomMedia?.blockingGet()
|
||||||
|
|
||||||
|
assertNotNull(randomMedia)
|
||||||
assertTrue(randomMedia is Media)
|
assertTrue(randomMedia is Media)
|
||||||
|
verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test scenario when all media is already nominated for deletion
|
* Test scenario when all media is already nominated for deletion
|
||||||
*/
|
*/
|
||||||
@Test(expected = Exception::class)
|
@Test(expected = RuntimeException::class)
|
||||||
fun getRandomMediaWithWithAllMediaNominatedForDeletion() {
|
fun getRandomMediaWithWithAllMediaNominatedForDeletion() {
|
||||||
val recentChange = getMockRecentChange("test", "File:Test1.jpeg", 0)
|
|
||||||
val recentChange1 = getMockRecentChange("test", "File:Test2.png", 0)
|
|
||||||
val recentChange2 = getMockRecentChange("test", "File:Test3.jpg", 0)
|
|
||||||
`when`(okHttpJsonApiClient?.recentFileChanges)
|
|
||||||
.thenReturn(Single.just(listOf(recentChange, recentChange1, recentChange2)))
|
|
||||||
|
|
||||||
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString()))
|
`when`(mediaWikiApi?.pageExists(ArgumentMatchers.anyString()))
|
||||||
.thenReturn(Single.just(true))
|
.thenReturn(Single.just(true))
|
||||||
reviewHelper?.randomMedia?.blockingGet()
|
val media = reviewHelper?.randomMedia?.blockingGet()
|
||||||
|
assertNull(media)
|
||||||
|
verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMockRecentChange(type: String, title: String, oldRevisionId: Long): RecentChange {
|
/**
|
||||||
|
* Test scenario when first media is already nominated for deletion
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun getRandomMediaWithWithOneMediaNominatedForDeletion() {
|
||||||
|
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test1.jpeg"))
|
||||||
|
.thenReturn(Single.just(true))
|
||||||
|
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test2.png"))
|
||||||
|
.thenReturn(Single.just(false))
|
||||||
|
`when`(mediaWikiApi?.pageExists("Commons:Deletion_requests/File:Test3.jpg"))
|
||||||
|
.thenReturn(Single.just(true))
|
||||||
|
|
||||||
|
val media = reviewHelper?.randomMedia?.blockingGet()
|
||||||
|
|
||||||
|
assertNotNull(media)
|
||||||
|
assertTrue(media is Media)
|
||||||
|
verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMockRecentChange(type: String, title: String, oldRevisionId: Long): RecentChange {
|
||||||
val recentChange = mock(RecentChange::class.java)
|
val recentChange = mock(RecentChange::class.java)
|
||||||
`when`(recentChange!!.type).thenReturn(type)
|
`when`(recentChange!!.type).thenReturn(type)
|
||||||
`when`(recentChange.title).thenReturn(title)
|
`when`(recentChange.title).thenReturn(title)
|
||||||
|
|
@ -88,9 +128,7 @@ class ReviewHelperTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
fun getFirstRevisionOfFile() {
|
fun getFirstRevisionOfFile() {
|
||||||
`when`(okHttpJsonApiClient?.getFirstRevisionOfFile(ArgumentMatchers.anyString()))
|
val firstRevisionOfFile = reviewHelper?.getFirstRevisionOfFile("Test.jpg")?.blockingFirst()
|
||||||
.thenReturn(Single.just(mock(MwQueryPage.Revision::class.java)))
|
|
||||||
val firstRevisionOfFile = reviewHelper?.getFirstRevisionOfFile("Test.jpg")?.blockingGet()
|
|
||||||
|
|
||||||
assertTrue(firstRevisionOfFile is MwQueryPage.Revision)
|
assertTrue(firstRevisionOfFile is MwQueryPage.Revision)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue