From 4e1cbbacc67601555e4ed6be89398f7fc31651e0 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 17:00:12 +0530 Subject: [PATCH 01/17] merged with master --- .travis.yml | 3 + app/build.gradle | 3 +- .../contributions/ContributionsFragment.java | 143 ++++---------- .../commons/contributions/MainActivity.java | 41 +--- .../commons/di/CommonsApplicationModule.java | 5 +- .../free/nrw/commons/di/NetworkingModule.java | 19 +- .../location/LocationServiceManager.java | 79 -------- .../commons/mwapi/OkHttpJsonApiClient.java | 108 +++-------- .../nrw/commons/nearby/NearbyFragment.java | 175 ++++-------------- .../nrw/commons/review/ReviewActivity.java | 19 +- .../free/nrw/commons/review/ReviewHelper.java | 127 ++++++++----- .../nrw/commons/review/ReviewInterface.java | 18 ++ .../fr/free/nrw/commons/settings/Prefs.java | 1 + .../commons/settings/SettingsFragment.java | 14 ++ .../LongTitleMultiSelectListPreference.java | 38 ++++ .../nrw/commons/upload/FileMetadataUtils.java | 43 +++++ .../nrw/commons/upload/FileProcessor.java | 66 ++++++- .../free/nrw/commons/upload/UploadModel.java | 2 +- .../nrw/commons/utils/PermissionUtils.java | 27 ++- app/src/main/res/values-ar/strings.xml | 11 ++ app/src/main/res/values-bn/strings.xml | 1 + app/src/main/res/values-fa/strings.xml | 63 ++++++- app/src/main/res/values-fr/strings.xml | 10 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 8 + app/src/main/res/values-mk/strings.xml | 10 + app/src/main/res/values-pt-rBR/strings.xml | 10 + app/src/main/res/values-ru/strings.xml | 10 + app/src/main/res/values-skr/strings.xml | 2 +- app/src/main/res/values-sv/strings.xml | 10 + app/src/main/res/values-uk/strings.xml | 10 + app/src/main/res/values-zh-rTW/strings.xml | 10 + app/src/main/res/values/arrays.xml | 21 +++ app/src/main/res/values/keys.xml | 9 + app/src/main/res/values/strings.xml | 16 +- app/src/main/res/xml/preferences.xml | 12 ++ app/src/test/data/exif_redact_sample.jpg | Bin 0 -> 164189 bytes .../nrw/commons/MediaDataExtractorTest.kt | 2 +- .../kotlin/fr/free/nrw/commons/MediaTest.kt | 2 +- .../free/nrw/commons/NearbyControllerTest.kt | 8 +- .../locations/BookMarkLocationDaoTest.kt | 2 +- .../pictures/BookmarkPictureDaoTest.kt | 2 +- .../nrw/commons/category/CategoryDaoTest.kt | 2 +- .../contributions/ContributionDaoTest.kt | 2 +- .../nrw/commons/delete/DeleteHelperTest.kt | 2 +- .../recentsearches/RecentSearchesDaoTest.kt | 2 +- .../modifications/ModifierSequenceDaoTest.kt | 2 +- .../mwapi/ApacheHttpClientMediaWikiApiTest.kt | 9 +- .../commons/mwapi/OkHttpJsonApiClientTest.kt | 2 +- .../nrw/commons/review/ReviewHelperTest.kt | 82 +++++--- .../commons/upload/FileMetadataUtilsTest.kt | 23 +++ .../nrw/commons/upload/FileProcessorTest.kt | 51 +++++ gradle.properties | 1 + 53 files changed, 776 insertions(+), 563 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.java create mode 100644 app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java create mode 100644 app/src/test/data/exif_redact_sample.jpg create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt diff --git a/.travis.yml b/.travis.yml index dcbf8137ed..ab511cecc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,3 +54,6 @@ before_install: - if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then openssl aes-256-cbc -K $encrypted_38ac1a5053f6_key -iv $encrypted_38ac1a5053f6_iv -in play.p12.enc -out play.p12 -d; fi +notifications: + webhooks: + - https://wiki-commons.zulipchat.com/api/v1/external/travis?api_key=kn4a8YKNqHCBYp7EW2k463txMj35vReq&stream=travis-ci diff --git a/app/build.gradle b/app/build.gradle index 8211d9db46..ec84df73e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,7 +60,8 @@ dependencies { testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION" testImplementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION" testImplementation 'junit:junit:4.12' - testImplementation 'org.robolectric:robolectric:3.7.1' + testImplementation 'org.robolectric:robolectric:4.3' + testImplementation 'androidx.test:core:1.2.0' testImplementation 'com.nhaarman:mockito-kotlin:1.5.0' testImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0' testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5" diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java index 6f95581b57..4d68500ca0 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java @@ -5,28 +5,26 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.PackageManager; import android.database.Cursor; import android.database.DataSetObserver; -import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Adapter; +import android.widget.CheckBox; +import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.cursoradapter.widget.CursorAdapter; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; -import androidx.cursoradapter.widget.CursorAdapter; -import androidx.appcompat.app.AlertDialog; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Adapter; -import android.widget.CheckBox; -import android.widget.Toast; import java.util.ArrayList; @@ -57,6 +55,7 @@ import fr.free.nrw.commons.upload.UploadService; import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.DialogUtil; +import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -66,7 +65,7 @@ import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI; -import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST; +import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION; import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; @@ -351,35 +350,6 @@ public void onAuthCookieAcquired(Intent uploadServiceIntent) { } - @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - Timber.d("onRequestPermissionsResult"); - switch (requestCode) { - case LOCATION_REQUEST: { - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Timber.d("Location permission granted, refreshing view"); - // No need to display permission request button anymore - locationManager.registerLocationManager(); - } else { - if (store.getBoolean("displayLocationPermissionForCardView", true)) { - // Still ask for permission - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableLocationPermission, - checkBoxView, - false); - } - } - } - break; - - default: - // This is needed to allow the request codes from the Fragments to be routed appropriately - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - } - } /** * Replace whatever is in the current contributionsFragmentContainer view with * mediaDetailPagerFragment, and preserve previous state in back stack. @@ -496,7 +466,7 @@ public void onResume() { if (store.getBoolean("displayNearbyCardView", true)) { - checkGPS(); + checkPermissionsAndShowNearbyCardView(); if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { nearbyNotificationCardView.setVisibility(View.VISIBLE); } @@ -509,77 +479,39 @@ public void onResume() { fetchCampaigns(); } - /** - * Check GPS to decide displaying request permission button or not. - */ - private void checkGPS() { - if (!locationManager.isProviderEnabled()) { - Timber.d("GPS is not enabled"); - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_GPS; - if (store.getBoolean("displayLocationPermissionForCardView", true)) { - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableGPS, - checkBoxView, - false); - } - } else { - Timber.d("GPS is enabled"); - checkLocationPermission(); + private void checkPermissionsAndShowNearbyCardView() { + if (PermissionUtils.hasPermission(getActivity(), Manifest.permission.ACCESS_FINE_LOCATION)) { + onLocationPermissionGranted(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) + && store.getBoolean("displayLocationPermissionForCardView", true) + && (((MainActivity) getActivity()).viewPager.getCurrentItem() == CONTRIBUTIONS_TAB_POSITION)) { + nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; + showNearbyCardPermissionRationale(); } } - private void checkLocationPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; - locationManager.registerLocationManager(); - } else { - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; - // If user didn't selected Don't ask again - if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) - && store.getBoolean("displayLocationPermissionForCardView", true)) { - DialogUtil.showAlertDialog(getActivity(), - getString(R.string.nearby_card_permission_title), - getString(R.string.nearby_card_permission_explanation), - this::displayYouWontSeeNearbyMessage, - this::enableLocationPermission, - checkBoxView, - false); - } - } - } else { - // If device is under Marshmallow, we already checked for GPS - nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; - locationManager.registerLocationManager(); - } + private void requestLocationPermission() { + PermissionUtils.checkPermissionsAndPerformAction(getActivity(), + Manifest.permission.ACCESS_FINE_LOCATION, + this::onLocationPermissionGranted, + this::displayYouWontSeeNearbyMessage, + -1, + -1); } - private void enableLocationPermission() { - if (!getActivity().isFinishing()) { - ((MainActivity) getActivity()).locationManager.requestPermissions(getActivity()); - } + private void onLocationPermissionGranted() { + nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; + locationManager.registerLocationManager(); } - private void enableGPS() { - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.gps_disabled) - .setCancelable(false) - .setPositiveButton(R.string.enable_gps, - (dialog, id) -> { - Intent callGPSSettingIntent = new Intent( - android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - Timber.d("Loaded settings page"); - ((MainActivity) getActivity()).startActivityForResult(callGPSSettingIntent, 1); - }) - .setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> { - dialog.cancel(); - displayYouWontSeeNearbyMessage(); - }) - .create() - .show(); + private void showNearbyCardPermissionRationale() { + DialogUtil.showAlertDialog(getActivity(), + getString(R.string.nearby_card_permission_title), + getString(R.string.nearby_card_permission_explanation), + this::displayYouWontSeeNearbyMessage, + this::requestLocationPermission, + checkBoxView, + false); } private void displayYouWontSeeNearbyMessage() { @@ -589,7 +521,6 @@ private void displayYouWontSeeNearbyMessage() { private void updateClosestNearbyCardViewInfo() { curLatLng = locationManager.getLastLocation(); - compositeDisposable.add(Observable.fromCallable(() -> nearbyController .loadAttractionsFromLocation(curLatLng, curLatLng, true, false)) // thanks to boolean, it will only return closest result .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index e7312edb83..6980579359 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -45,7 +45,6 @@ import timber.log.Timber; import static android.content.ContentResolver.requestSync; -import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST; public class MainActivity extends AuthenticatedActivity implements FragmentManager.OnBackStackChangedListener { @@ -68,8 +67,8 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag public boolean isAuthCookieAcquired = false; public ContributionsActivityPagerAdapter contributionsActivityPagerAdapter; - public final int CONTRIBUTIONS_TAB_POSITION = 0; - public final int NEARBY_TAB_POSITION = 1; + public static final int CONTRIBUTIONS_TAB_POSITION = 0; + public static final int NEARBY_TAB_POSITION = 1; public boolean isContributionsFragmentVisible = true; // False means nearby fragment is visible private Menu menu; @@ -361,12 +360,6 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - private boolean deviceHasCamera() { - PackageManager pm = getPackageManager(); - return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA) || - pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); - } - public class ContributionsActivityPagerAdapter extends FragmentPagerAdapter { FragmentManager fragmentManager; private boolean isContributionsListFragment = true; // to know what to put in first tab, Contributions of Media Details @@ -450,15 +443,6 @@ public String makeFragmentName(int viewId, int 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 @@ -468,27 +452,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { controller.handleActivityResult(this, requestCode, resultCode, data); } - @Override - public void onRequestPermissionsResult(int requestCode, - String[] permissions, int[] grantResults) { - if (requestCode == LOCATION_REQUEST) { - // If request is cancelled, the result arrays are empty. - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Timber.d("Location permission given"); - ((ContributionsFragment)contributionsActivityPagerAdapter - .getItem(0)).locationManager.registerLocationManager(); - } else { - // If nearby fragment is visible and location permission is not given, send user back to contrib fragment - if (!isContributionsFragmentVisible) { - viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION); - - // TODO: If contrib fragment is visible and location permission is not given, display permission request button - } - } - } - } - @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 8df5b83ab7..0ebb04f50b 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -3,11 +3,12 @@ import android.app.Activity; import android.content.ContentProviderClient; import android.content.Context; -import androidx.collection.LruCache; import android.view.inputmethod.InputMethodManager; import com.google.gson.Gson; +import org.wikipedia.dataclient.WikiSite; + import io.reactivex.Scheduler; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -19,6 +20,7 @@ import javax.inject.Named; import javax.inject.Singleton; +import androidx.collection.LruCache; import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.BuildConfig; @@ -27,7 +29,6 @@ import fr.free.nrw.commons.auth.SessionManager; 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.location.LocationServiceManager; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.upload.UploadController; diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java index f66ae65276..c8664cd9fb 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -4,6 +4,8 @@ import com.google.gson.Gson; +import org.wikipedia.dataclient.ServiceFactory; +import org.wikipedia.dataclient.WikiSite; import org.wikipedia.json.GsonUtil; import java.io.File; @@ -20,6 +22,7 @@ import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; +import fr.free.nrw.commons.review.ReviewInterface; import okhttp3.Cache; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -70,7 +73,7 @@ public MediaWikiApi provideMediaWikiApi(Context context, @Provides @Singleton public OkHttpJsonApiClient provideOkHttpJsonApiClient(OkHttpClient okHttpClient, - @Named("tools_force") HttpUrl toolsForgeUrl, + @Named("tools_forge") HttpUrl toolsForgeUrl, @Named("default_preferences") JsonKvStore defaultKvStore, Gson gson) { return new OkHttpJsonApiClient(okHttpClient, @@ -91,7 +94,7 @@ public String provideMwApiUrl() { } @Provides - @Named("tools_force") + @Named("tools_forge") @NonNull @SuppressWarnings("ConstantConditions") public HttpUrl provideToolsForgeUrl() { @@ -108,4 +111,16 @@ public Gson provideGson() { 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); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java index 68b7c742dc..8e6705ecbc 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java @@ -1,19 +1,12 @@ package fr.free.nrw.commons.location; -import android.Manifest; -import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; -import android.content.pm.PackageManager; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; -import androidx.core.content.ContextCompat; - import java.util.HashSet; import java.util.List; import java.util.Set; @@ -44,78 +37,6 @@ public LocationServiceManager(Context context) { this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } - /** - * Returns the current status of the location provider. - * - * @return true if the location provider is enabled - */ - public boolean isProviderEnabled() { - return (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)); - } - - /** - * Returns whether the location permission is granted. - * @return true if the location permission is granted - */ - public boolean isLocationPermissionGranted(@NonNull Context context) { - return ContextCompat.checkSelfPermission(context, - Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; - } - - /** - * Requests the location permission to be granted. - * - * @param activity the activity - */ - public void requestPermissions(Activity activity) { - if (activity.isFinishing()) { - return; - } - ActivityCompat.requestPermissions(activity, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - LOCATION_REQUEST); - } - - /** - * The permission explanation dialog box is now displayed just once for a particular activity. We are subscribing - * to updates from multiple providers so its important to show the dialog just once. Otherwise it will be displayed - * once for every provider, which in our case currently is 2. - * @param activity - * @return - */ - public boolean isPermissionExplanationRequired(Activity activity) { - if (activity.isFinishing()) { - return false; - } - boolean showRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION); - if (showRequestPermissionRationale && !locationExplanationDisplayed.contains(activity)) { - locationExplanationDisplayed.add(activity); - return true; - } - return false; - } - - /** - * Gets the last known location in cases where there wasn't time to register a listener - * (e.g. when Location permission just granted) - * @return last known LatLng - */ - @SuppressLint("MissingPermission") - public LatLng getLKL(@NonNull Context context) { - if (isLocationPermissionGranted(context)) { - Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (lastKL == null) { - lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - } - if (lastKL == null) { - return null; - } - return LatLng.from(lastKL); - } else { - return null; - } - } - public LatLng getLastLocation() { if (lastLocation == null) { return null; diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java index 6f13f7e49b..e829e946fd 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/OkHttpJsonApiClient.java @@ -1,15 +1,17 @@ package fr.free.nrw.commons.mwapi; +import android.text.TextUtils; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.apache.commons.lang3.StringUtils; import org.wikipedia.dataclient.mwapi.MwQueryPage; import org.wikipedia.dataclient.mwapi.MwQueryResponse; -import org.wikipedia.dataclient.mwapi.RecentChange; -import org.wikipedia.util.DateUtil; + import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; @@ -17,7 +19,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Random; import javax.inject.Inject; import javax.inject.Singleton; @@ -33,6 +34,7 @@ import fr.free.nrw.commons.nearby.model.NearbyResultItem; import fr.free.nrw.commons.upload.FileUtils; import fr.free.nrw.commons.utils.CommonsDateUtil; +import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.wikidata.model.GetWikidataEditCountResponse; import io.reactivex.Observable; import io.reactivex.Single; @@ -82,8 +84,13 @@ public OkHttpJsonApiClient(OkHttpClient okHttpClient, public Single getUploadCount(String userName) { HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder(); urlBuilder - .addPathSegments("/uploadsbyuser.py") + .addPathSegments("uploadsbyuser.py") .addQueryParameter("user", userName); + + if (ConfigUtils.isBetaFlavour()) { + urlBuilder.addQueryParameter("labs", "commonswiki"); + } + Request request = new Request.Builder() .url(urlBuilder.build()) .build(); @@ -91,7 +98,9 @@ public Single getUploadCount(String userName) { return Single.fromCallable(() -> { Response response = okHttpClient.newCall(request).execute(); if (response != null && response.isSuccessful()) { - return Integer.parseInt(response.body().string().trim()); + if(!TextUtils.isEmpty(response.body().string().trim())){ + return Integer.parseInt(response.body().string().trim()); + } } return 0; }); @@ -101,8 +110,13 @@ public Single getUploadCount(String userName) { public Single getWikidataEdits(String userName) { HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder(); urlBuilder - .addPathSegments("/wikidataedits.py") + .addPathSegments("wikidataedits.py") .addQueryParameter("user", userName); + + if (ConfigUtils.isBetaFlavour()) { + urlBuilder.addQueryParameter("labs", "commonswiki"); + } + Request request = new Request.Builder() .url(urlBuilder.build()) .build(); @@ -116,7 +130,9 @@ public Single getWikidataEdits(String userName) { return 0; } GetWikidataEditCountResponse countResponse = gson.fromJson(json, GetWikidataEditCountResponse.class); - return countResponse.getWikidataEditCount(); + if (null != countResponse) { + return countResponse.getWikidataEditCount(); + } } return 0; }); @@ -131,7 +147,7 @@ public Single getWikidataEdits(String userName) { */ public Single getAchievements(String userName) { final String fetchAchievementUrlTemplate = - wikiMediaToolforgeUrl + "/feedback.py"; + wikiMediaToolforgeUrl + (ConfigUtils.isBetaFlavour() ? "/feedback.py?labs=commonswiki" : "/feedback.py"); return Single.fromCallable(() -> { String url = String.format( Locale.ENGLISH, @@ -405,80 +421,4 @@ private void putContinueValues(String keyword, Map values) { private Map getContinueValues(String keyword) { return defaultKvStore.getJson("query_continue_" + keyword, mapType); } - - /** - * Returns recent changes on commons - * - * @return list of recent changes made - */ - @Nullable - public Single> 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 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; - }); - } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java index 100ca733b8..062f7da906 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java @@ -1,20 +1,11 @@ package fr.free.nrw.commons.nearby; +import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.os.Build; import android.os.Bundle; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.constraintlayout.widget.ConstraintLayout; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.snackbar.Snackbar; - -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.appcompat.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -22,12 +13,19 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.snackbar.Snackbar; import com.google.gson.Gson; import java.util.List; import javax.inject.Inject; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; @@ -38,6 +36,7 @@ import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.utils.FragmentUtils; import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.wikidata.WikidataEditListener; import io.reactivex.Observable; @@ -45,6 +44,8 @@ import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION; +import static fr.free.nrw.commons.contributions.MainActivity.NEARBY_TAB_POSITION; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; @@ -193,8 +194,8 @@ private void initBottomSheetBehaviour() { bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { @Override - public void onStateChanged(View bottomSheet, int newState) { - prepareViewsForSheetPosition(newState); + public void onStateChanged(View bottomSheet, int unusedNewState) { + prepareViewsForSheetPosition(); } @Override @@ -210,9 +211,8 @@ public void onSlide(View bottomSheet, float slideOffset) { /** * Sets camera position, zoom level according to sheet positions - * @param bottomSheetState expanded, collapsed or hidden */ - public void prepareViewsForSheetPosition(int bottomSheetState) { + private void prepareViewsForSheetPosition() { // TODO } @@ -246,7 +246,7 @@ public void onWikidataEditSuccessful() { * * @param locationChangeType defines if location changed significantly or slightly */ - public void refreshView(LocationServiceManager.LocationChangeType locationChangeType) { + private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) { Timber.d("Refreshing nearby places"); if (lockNearbyView) { return; @@ -324,7 +324,7 @@ public void refreshView(LocationServiceManager.LocationChangeType locationChange * button. It populates places for custom location. * @param customLatLng Custom area which we will search around */ - public void refreshViewForCustomLocation(LatLng customLatLng, boolean refreshForCurrentLocation) { + void refreshViewForCustomLocation(LatLng customLatLng, boolean refreshForCurrentLocation) { if (customLatLng == null) { // If null, return return; @@ -568,130 +568,7 @@ private void hideProgressBar() { * This method first checks if the location permissions has been granted and then register the location manager for updates. */ private void registerLocationUpdates() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - locationManager.registerLocationManager(); - } else { - // Should we show an explanation? - if (locationManager.isPermissionExplanationRequired(getActivity())) { - new AlertDialog.Builder(getActivity()) - .setMessage(getString(R.string.location_permission_rationale_nearby)) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - requestLocationPermissions(); - dialog.dismiss(); - }) - .setNegativeButton(android.R.string.cancel, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - - } else { - // No explanation needed, we can request the permission. - requestLocationPermissions(); - } - } - } else { - locationManager.registerLocationManager(); - } - } - - /** - * Requests location permission if activity is not null - */ - private void requestLocationPermissions() { - if (!getActivity().isFinishing()) { - locationManager.requestPermissions(getActivity()); - } - } - - /** - * Will warn user if location is denied - */ - private void showLocationPermissionDeniedErrorDialog() { - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.nearby_needs_permissions) - .setCancelable(false) - .setPositiveButton(R.string.give_permission, (dialog, which) -> { - //will ask for the location permission again - checkGps(); - }) - .setNegativeButton(R.string.cancel, (dialog, which) -> { - //dismiss dialog and send user to contributions tab instead - dialog.cancel(); - ((MainActivity)getActivity()).viewPager.setCurrentItem(((MainActivity)getActivity()).CONTRIBUTIONS_TAB_POSITION); - }) - .create() - .show(); - } - - /** - * Checks device GPS permission first for all API levels - */ - private void checkGps() { - Timber.d("checking GPS"); - if (!locationManager.isProviderEnabled()) { - Timber.d("GPS is not enabled"); - new AlertDialog.Builder(getActivity()) - .setMessage(R.string.gps_disabled) - .setCancelable(false) - .setPositiveButton(R.string.enable_gps, - (dialog, id) -> { - Intent callGPSSettingIntent = new Intent( - android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS); - Timber.d("Loaded settings page"); - startActivityForResult(callGPSSettingIntent, 1); - }) - .setNegativeButton(R.string.menu_cancel_upload, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - } else { - Timber.d("GPS is enabled"); - checkLocationPermission(); - } - } - - /** - * This method ideally should be called from inside of CheckGPS method. If device GPS is enabled - * then we need to control app specific permissions for >=M devices. For other devices, enabled - * GPS is enough for nearby, so directly call refresh view. - */ - private void checkLocationPermission() { - Timber.d("Checking location permission"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (locationManager.isLocationPermissionGranted(requireContext())) { - refreshView(LOCATION_SIGNIFICANTLY_CHANGED); - } else { - // Should we show an explanation? - if (locationManager.isPermissionExplanationRequired(getActivity())) { - // Show an explanation to the user *asynchronously* -- don't block - // this thread waiting for the user's response! After the user - // sees the explanation, try again to request the permission. - new AlertDialog.Builder(getActivity()) - .setMessage(getString(R.string.location_permission_rationale_nearby)) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { - requestLocationPermissions(); - dialog.dismiss(); - }) - .setNegativeButton(android.R.string.cancel, (dialog, id) -> { - showLocationPermissionDeniedErrorDialog(); - dialog.cancel(); - }) - .create() - .show(); - - } else { - // No explanation needed, we can request the permission. - requestLocationPermissions(); - } - } - } else { - refreshView(LOCATION_SIGNIFICANTLY_CHANGED); - } + locationManager.registerLocationManager(); } private void showErrorMessage(String message) { @@ -748,8 +625,12 @@ public void onReceive(Context context, Intent intent) { public void onResume() { super.onResume(); // Resume the fragment if exist - resumeFragment(); + if (((MainActivity) getActivity()).viewPager.getCurrentItem() == NEARBY_TAB_POSITION) { + checkPermissionsAndPerformAction(this::resumeFragment); + } else { + resumeFragment(); } + } /** * Perform nearby operations on nearby tab selected @@ -758,8 +639,16 @@ public void onResume() { public void onTabSelected(boolean onOrientationChanged) { Timber.d("On nearby tab selected"); this.onOrientationChanged = onOrientationChanged; - performNearbyOperations(); + checkPermissionsAndPerformAction(this::performNearbyOperations); + } + private void checkPermissionsAndPerformAction(Runnable runnable) { + PermissionUtils.checkPermissionsAndPerformAction(getActivity(), + Manifest.permission.ACCESS_FINE_LOCATION, + runnable, + () -> ((MainActivity) getActivity()).viewPager.setCurrentItem(CONTRIBUTIONS_TAB_POSITION), + R.string.location_permission_title, + R.string.location_permission_rationale_nearby); } /** @@ -769,8 +658,8 @@ private void performNearbyOperations() { locationManager.addLocationListener(this); registerLocationUpdates(); lockNearbyView = false; - checkGps(); addNetworkBroadcastReceiver(); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java index 7f029bc6f4..cbb9848ca7 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.java @@ -12,13 +12,20 @@ import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; + import androidx.appcompat.widget.Toolbar; import androidx.drawerlayout.widget.DrawerLayout; -import butterknife.BindView; -import butterknife.ButterKnife; + import com.facebook.drawee.view.SimpleDraweeView; import com.google.android.material.navigation.NavigationView; 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.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; @@ -29,8 +36,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; -import java.util.ArrayList; -import javax.inject.Inject; public class ReviewActivity extends AuthenticatedActivity { @@ -139,7 +144,11 @@ public boolean runRandomizer() { compositeDisposable.add(reviewHelper.getRandomMedia() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::updateImage)); + .subscribe(media -> { + if (media != null) { + updateImage(media); + } + })); return true; } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java index 7468cc7f6e..30903a89a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewHelper.java @@ -1,39 +1,63 @@ package fr.free.nrw.commons.review; + +import org.apache.commons.lang3.StringUtils; import org.wikipedia.dataclient.mwapi.MwQueryPage; import org.wikipedia.dataclient.mwapi.RecentChange; +import org.wikipedia.util.DateUtil; -import java.util.List; +import java.util.Date; import java.util.Random; import javax.inject.Inject; import javax.inject.Singleton; -import androidx.annotation.Nullable; -import androidx.core.util.Pair; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; +import io.reactivex.Observable; import io.reactivex.Single; @Singleton public class ReviewHelper { - private static final int MAX_RANDOM_TRIES = 5; private static final String[] imageExtensions = new String[]{".jpg", ".jpeg", ".png"}; private final OkHttpJsonApiClient okHttpJsonApiClient; private final MediaWikiApi mediaWikiApi; + private final ReviewInterface reviewInterface; @Inject - public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient, MediaWikiApi mediaWikiApi) { + public ReviewHelper(OkHttpJsonApiClient okHttpJsonApiClient, + MediaWikiApi mediaWikiApi, + ReviewInterface reviewInterface) { this.okHttpJsonApiClient = okHttpJsonApiClient; this.mediaWikiApi = mediaWikiApi; + this.reviewInterface = reviewInterface; } - Single getRandomMedia() { - return getRandomFileChange() - .flatMap(fileName -> okHttpJsonApiClient.getMedia(fileName, false)); + /** + * Fetches recent changes from MediaWiki API + * 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 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 @@ Single getRandomMedia() { * - Checks if the file is nominated for deletion * - Retries upto 5 times for getting a file which is not nominated for deletion * + * @return Random file change + */ + public Single 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 */ - private Single getRandomFileChange() { - return okHttpJsonApiClient.getRecentFileChanges() - .map(this::findImageInRecentChanges) - .flatMap(title -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + title) - .map(pageExists -> new Pair<>(title, pageExists))) - .map((Pair pair) -> { - if (!pair.second) { - return pair.first; + private Single getRandomMediaFromRecentChange(RecentChange recentChange) { + return Single.just(recentChange) + .flatMap(change -> mediaWikiApi.pageExists("Commons:Deletion_requests/" + change.getTitle())) + .flatMap(isDeleted -> { + if (isDeleted) { + return Single.just(new Media("")); } - throw new Exception("Already nominated for deletion"); - }).retry(MAX_RANDOM_TRIES); + return okHttpJsonApiClient.getMedia(recentChange.getTitle(), false); + }); + } - Single getFirstRevisionOfFile(String fileName) { - return okHttpJsonApiClient.getFirstRevisionOfFile(fileName); + /** + * Gets the first revision of the file from filename + * @param filename + * @return + */ + Observable getFirstRevisionOfFile(String filename) { + return reviewInterface.getFirstRevisionOfFile(filename) + .map(response -> response.query().firstPage().revisions().get(0)); } - @Nullable - private String findImageInRecentChanges(List recentChanges) { - String imageTitle; - Random r = new Random(); - int count = recentChanges.size(); - // Build a range array - int[] randomIndexes = new int[count]; - for (int i = 0; i < count; i++) { - randomIndexes[i] = i; - } - // 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; + /** + * Checks if the change is reviewable or not. + * - checks the type and revisionId of the change + * - checks supported image extensions + * @param recentChange + * @return + */ + private boolean isChangeReviewable(RecentChange recentChange) { + if (recentChange.getType().equals("log") && !(recentChange.getOldRevisionId() == 0)) { + return false; } - 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) { - if (imageTitle.toLowerCase().endsWith(imageExtension)) { - return imageTitle; - } + for (String extension : imageExtensions) { + if (recentChange.getTitle().endsWith(extension)) { + return true; } } - return null; + + return false; } } diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.java b/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.java new file mode 100644 index 0000000000..7dee125fc3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewInterface.java @@ -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 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 getFirstRevisionOfFile(@Query("titles") String titles); +} diff --git a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java index d059c862e6..9a40acc55f 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java @@ -7,6 +7,7 @@ public class Prefs { public static final String DEFAULT_LICENSE = "defaultLicense"; public static final String UPLOADS_SHOWING = "uploadsshowing"; public static final String IS_CONTRIBUTION_COUNT_CHANGED = "ccontributionCountChanged"; + public static final String MANAGED_EXIF_TAGS = "managedExifTags"; public static class Licenses { public static final String CC_BY_SA_3 = "CC BY-SA 3.0"; diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index cf0e8aaeea..6d527c91da 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -4,6 +4,7 @@ import android.net.Uri; import android.os.Bundle; import android.preference.EditTextPreference; +import android.preference.MultiSelectListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.SwitchPreference; @@ -14,6 +15,11 @@ import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.single.BasePermissionListener; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import javax.inject.Inject; import javax.inject.Named; @@ -59,6 +65,14 @@ public void onCreate(Bundle savedInstanceState) { return true; }); + MultiSelectListPreference multiSelectListPref = (MultiSelectListPreference) findPreference("manageExifTags"); + if (multiSelectListPref != null) { + multiSelectListPref.setOnPreferenceChangeListener((preference, newValue) -> { + defaultKvStore.putJson(Prefs.MANAGED_EXIF_TAGS, newValue); + return true; + }); + } + final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads"); int currentUploadLimit = defaultKvStore.getInt(Prefs.UPLOADS_SHOWING, 100); uploadLimit.setText(Integer.toString(currentUploadLimit)); diff --git a/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java b/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java new file mode 100644 index 0000000000..9c5f327ac5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/ui/LongTitlePreferences/LongTitleMultiSelectListPreference.java @@ -0,0 +1,38 @@ +package fr.free.nrw.commons.ui.LongTitlePreferences; + +import android.content.Context; +import android.preference.MultiSelectListPreference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +public class LongTitleMultiSelectListPreference extends MultiSelectListPreference { + /* + public LongTitleMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public LongTitleMultiSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + */ + public LongTitleMultiSelectListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public LongTitleMultiSelectListPreference(Context context) { + super(context); + } + + @Override + protected void onBindView(View view) + { + super.onBindView(view); + + TextView title= view.findViewById(android.R.id.title); + if (title != null) { + title.setSingleLine(false); + } + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java new file mode 100644 index 0000000000..70dec35a4c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileMetadataUtils.java @@ -0,0 +1,43 @@ +package fr.free.nrw.commons.upload; + +import timber.log.Timber; + +import static androidx.exifinterface.media.ExifInterface.*; + +/** + * Support utils for EXIF metadata handling + * + */ +public class FileMetadataUtils { + + /** + * Takes EXIF label from sharedPreferences as input and returns relevant EXIF tags + * + * @param pref EXIF sharedPreference label + * @return EXIF tags + */ + public static String[] getTagsFromPref(String pref) { + Timber.d("Retuning tags for pref:%s", pref); + switch (pref) { + case "Author": + return new String[]{TAG_ARTIST, TAG_CAMARA_OWNER_NAME}; + case "Copyright": + return new String[]{TAG_COPYRIGHT}; + case "Location": + return new String[]{TAG_GPS_LATITUDE, TAG_GPS_LATITUDE_REF, + TAG_GPS_LONGITUDE, TAG_GPS_LONGITUDE_REF, + TAG_GPS_ALTITUDE, TAG_GPS_ALTITUDE_REF}; + case "Camera Model": + return new String[]{TAG_MAKE, TAG_MODEL}; + case "Lens Model": + return new String[]{TAG_LENS_MAKE, TAG_LENS_MODEL, TAG_LENS_SPECIFICATION}; + case "Serial Numbers": + return new String[]{TAG_BODY_SERIAL_NUMBER, TAG_LENS_SERIAL_NUMBER}; + case "Software": + return new String[]{TAG_SOFTWARE}; + default: + return new String[]{}; + } + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java index 72d8f14d22..e9922332c4 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java @@ -2,23 +2,35 @@ import android.annotation.SuppressLint; import android.content.ContentResolver; +import android.content.Context; import android.net.Uri; import androidx.annotation.NonNull; import fr.free.nrw.commons.upload.SimilarImageDialogFragment.Callback; import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; import androidx.exifinterface.media.ExifInterface; + +import com.google.gson.reflect.TypeToken; + +import fr.free.nrw.commons.R; import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.mwapi.CategoryApi; +import fr.free.nrw.commons.settings.Prefs; +import io.reactivex.Observable; import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; @@ -67,7 +79,10 @@ void initFileDetails(@NonNull String filePath, ContentResolver contentResolver) /** * Processes filePath coordinates, either from EXIF data or user location */ - GPSExtractor processFileCoordinates(SimilarImageInterface similarImageInterface) { + GPSExtractor processFileCoordinates(SimilarImageInterface similarImageInterface, Context context) { + // Redact EXIF data as indicated in preferences. + redactExifTags(exifInterface, getExifTagsToRedact(context)); + Timber.d("Calling GPSExtractor"); imageObj = new GPSExtractor(exifInterface); decimalCoords = imageObj.getCoords(); @@ -82,6 +97,55 @@ GPSExtractor processFileCoordinates(SimilarImageInterface similarImageInterface) return imageObj; } + /** + * Gets EXIF Tags from preferences to be redacted. + * + * @param context application context + * @return tags to be redacted + */ + private Set getExifTagsToRedact(Context context) { + Type setType = new TypeToken>() {}.getType(); + Set prefManageEXIFTags = defaultKvStore.getJson(Prefs.MANAGED_EXIF_TAGS, setType); + + Set redactTags = new HashSet<>(Arrays.asList( + context.getResources().getStringArray(R.array.pref_exifTag_values))); + Timber.d(redactTags.toString()); + + if (prefManageEXIFTags != null) redactTags.removeAll(prefManageEXIFTags); + + return redactTags; + } + + /** + * Redacts EXIF metadata as indicated in preferences. + * + * @param exifInterface ExifInterface object + * @param redactTags tags to be redacted + */ + public static void redactExifTags(ExifInterface exifInterface, Set redactTags) { + if(redactTags.isEmpty()) return; + + Disposable disposable = Observable.fromIterable(redactTags) + .flatMap(tag -> Observable.fromArray(FileMetadataUtils.getTagsFromPref(tag))) + .forEach(tag -> { + Timber.d("Checking for tag: %s", tag); + String oldValue = exifInterface.getAttribute(tag); + if (oldValue != null && !oldValue.isEmpty()) { + Timber.d("Exif tag %s with value %s redacted.", tag, oldValue); + exifInterface.setAttribute(tag, null); + } + }); + CompositeDisposable disposables = new CompositeDisposable(); + disposables.add(disposable); + disposables.clear(); + + try { + exifInterface.saveAttributes(); + } catch (IOException e) { + Timber.w("EXIF redaction failed: %s", e.toString()); + } + } + /** * Find other images around the same location that were taken within the last 20 sec * @param similarImageInterface diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index 9930e2c08b..4cbdb674b9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -149,7 +149,7 @@ private UploadItem getUploadItem(UploadableFile uploadableFile, createdTimestampSource = dateTimeWithSource.getSource(); } Timber.d("File created date is %d", fileCreatedDate); - GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface); + GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface,context); UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(), Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate, createdTimestampSource); diff --git a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java index c4268790cb..8d258256a7 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.java @@ -5,17 +5,17 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; -import androidx.annotation.StringRes; -import androidx.core.content.ContextCompat; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; + import com.karumi.dexter.Dexter; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionDeniedResponse; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.single.BasePermissionListener; + import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; @@ -27,7 +27,7 @@ public class PermissionUtils { It open the app settings from where the user can manually give us the required permission. * @param activity */ - public static void askUserToManuallyEnablePermissionFromSettings(Activity activity) { + private static void askUserToManuallyEnablePermissionFromSettings(Activity activity) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", activity.getPackageName(), null); intent.setData(uri); @@ -50,6 +50,9 @@ public static boolean hasPermission(Activity activity, String permission) { * Checks for a particular permission and runs the runnable to perform an action when the permission is granted * Also, it shows a rationale if needed * + * rationaleTitle and rationaleMessage can be invalid @StringRes. If the value is -1 then no permission rationale + * will be displayed and permission would be requested + * * Sample usage: * * PermissionUtils.checkPermissionsAndPerformAction(activity, @@ -58,12 +61,20 @@ public static boolean hasPermission(Activity activity, String permission) { * R.string.storage_permission_title, * R.string.write_storage_permission_rationale); * + * If you don't want the permission rationale to be shown then use: + * + * PermissionUtils.checkPermissionsAndPerformAction(activity, + * Manifest.permission.WRITE_EXTERNAL_STORAGE, + * () -> initiateCameraUpload(activity), + * - 1, -1); + * + * * * @param activity activity requesting permissions * @param permission the permission being requests * @param onPermissionGranted the runnable to be executed when the permission is granted - * @param rationaleTitle rationale title to be displayed when permission was denied - * @param rationaleMessage rationale message to be displayed when permission was denied + * @param rationaleTitle rationale title to be displayed when permission was denied. It can be an invalid @StringRes + * @param rationaleMessage rationale message to be displayed when permission was denied. It can be an invalid @StringRes */ public static void checkPermissionsAndPerformAction(Activity activity, String permission, Runnable onPermissionGranted, @StringRes int rationaleTitle, @@ -93,7 +104,6 @@ public static void checkPermissionsAndPerformAction(Activity activity, String pe * @param rationaleTitle rationale title to be displayed when permission was denied * @param rationaleMessage rationale message to be displayed when permission was denied */ - public static void checkPermissionsAndPerformAction(Activity activity, String permission, Runnable onPermissionGranted, Runnable onPermissionDenied, @StringRes int rationaleTitle, @StringRes int rationaleMessage) { @@ -120,6 +130,10 @@ public static void checkPermissionsAndPerformAction(Activity activity, String pe @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) { + if (rationaleTitle == -1 && rationaleMessage == -1) { + token.continuePermissionRequest(); + return; + } DialogUtil.showAlertDialog(activity, activity.getString(rationaleTitle), activity.getString(rationaleMessage), activity.getString(android.R.string.ok), @@ -129,4 +143,5 @@ public void onPermissionRationaleShouldBeShown(PermissionRequest permission, }) .check(); } + } diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a45de752b2..8185aab5b2 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -6,6 +6,7 @@ * Azouz.anis * ButterflyOfFire * Claw eg +* Kassem7899 * Meno25 * Mido * Monrokhoury @@ -22,6 +23,7 @@ المظهر عام التعليقات + الخصوصية الموقع كومنز @@ -516,6 +518,15 @@ أمثلة على صور لعدم رفعها تخطي هذه الصورة التنزيل فشل!!. لا يمكننا تنزيل الملف دون إذن تخزين خارجي. + إدارة وسوم EXIF + حدد أية وسوم EXIF ​​لتحتفظ بها في المرفوعات + المؤلف + حقوق نشر + الموقع + طراز الكاميرا + طراز العدسة + الأرقام التسلسلية + برمجية ارفع الصور لويكيميديا ​​كومنز على هاتفك قم بتنزيل تطبيق كومنز: %1$s مشاركة التطبيق عبر... معلومات الصورة diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 9b436a0715..0d9714aba3 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -322,4 +322,5 @@ হ্যাঁ, জমা দিন না, ফিরে যান অনুগ্রহ করে অপেক্ষা করুন... + অবস্থান diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index f6876812f2..0000912742 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -1,5 +1,6 @@ کاوش @@ -24,6 +28,7 @@ نمایش صفحه عمومی بازخورد + حریم خصوصی مکان ویکی‌انبار @@ -175,13 +180,15 @@ بدون بحث مجوز ناشناخته تازه‌کردن + درخواست اجازهٔ ذخیره‌سازی اجازه‌های مورد نیاز: مطالعهٔ حافظهٔ خارجی. اپلیکیشن بدون آن نمی‌تواند کار کند. - اجازه‌های مورد نیاز: نوشتن در حافظهٔ خارجی. اپلیکیشن نمیتواند به دبین دسترسی داشته باشد. + اجازه‌های مورد نیاز: نوشتن در حافظهٔ خارجی. اپلیکیشن نمی‌تواند به دوربین/گالری دسترسی داشته باشد. اجازه‌های اختیاری: دریافت موقعیت برای پیشنهاد رده تأیید مکان‌‌های اطراف مکانی در نزدیکی یافت نشد هشدار + پرونده در ویکی‌انبار موجود است. آیا مطمئنید که می‌خواهید ادامه دهید؟ بله خیر عنوان @@ -201,7 +208,8 @@ ورودی نامعتبر عدم توانایی در نمایش بیش از ۵۰۰ مورد عدد معتبر را وارد کنید - تنظیم محدودیت بارگذاری‌های اخیر + حد بارگذاری نمی‌تواند ۰ باشد + محدودیت بارگذاری اخیر تأیید دومرحله‌ای الان پشتیبانی نمی‌شود. آیا واقعاً قصد خروج از سامانه را دارید؟ نشان ویکی‌انبار @@ -245,11 +253,16 @@ خطا در زمان دریافت تصاویر عنوانی توصیفی و یکتا برای پرونده که به عنوان نام پرونده در نظر گرفته خواهد شد. ترجیحاً به زبان ساده باشد، می‌توانید فاصله هم به کار ببرید. پسوند پرونده را ننویسید. لطفاً تصویر را تا حد توان شرح دهید. کجا گرفته شده‌است؟ شامل چه چیزی می‌شود؟ لطفاً اشیا یا افراد را شرح دهید. اطلاعاتی که به راحتی قابل مشاهده هستند را صرفه‌نظر کنید. اگر چیزی در تصویر غیر طبیعی به نظر می‌رسد آن را شرح دهید. + این تصویر خیلی تار است. آیا مطمئنید که می‌خواهید آن را بارگذاری کنید؟ ویکی‌انبار فقط برای نگهداری از تصاویری است که ارزش دانشنامه‌ای داشته باشند. + این تصویر خیلی محو است. آیا مطمئنید که می‌خواهید آن را بارگذاری کنید؟ ویکی‌انبار فقط برای نگهداری از تصاویری است که ارزش دانشنامه‌ای داشته باشند. + مشکلات احتمالی با این تصویر : تصویر بیش از حد تاریک است. تصویر تار است. تصویر اکنون در انبار موجود است. این تصویر در مکان متفاوتی گرفته شده است. + لطفاً فقط تصاویری را بارگذاری کنید که خودتان گرفته‌اید. تصاویری را که در فیس‌بوک دیگران پیدا کرده‌اید بارگذاری نکنید. هنوزم می خوای این عکس رو آپلود کنی؟ + لطفاً فقط تصاویری را بارگذاری کنید که خودتان گرفته‌اید. تصاویری را که از اینترنت بارگیری کرده‌اید بارگذاری نکنید. اجازه بده استفاده از حافظهٔ خارجی ذخیرهٔ تصویرهای گرفته شده توسط دوربین درونکار اپلیکیشن بر روی دستگاه شما @@ -261,6 +274,8 @@ نامزد شده برای حذف این تصویر برای حذف شدن علامت گذاری شده. . + نامزد حذف کردن %1$s. + نامزد حذف کردن: %1$s مشاهده در مرورگر رها کردن ورود به سامانه @@ -268,6 +283,7 @@ برای بارگذاری تصاویر در آینده شما باید وارد حساب کاربری خود شوید. لطفا برای استفاده از این ویژگی وارد شوید از ویکی واژه به کلیپ برد کپی کنید. + ویکی‌متن در کلیپ‌بورد کپی شد مکان تغییر نکرده‌است. مکان موجود نیست. برای نمایش مکان‌ّای اطراف نیاز به اجازه است. @@ -288,9 +304,12 @@ اینترنت در دسترس نیست اینترنت در دسترس است خطا در آوردن اطلاعیه + در تلاش برای بدست‌آوردن تصویر جهت بازبینی خطایی پیش آمد. لطفا برای تلاش مجدد بر روی ریفرش کلیک کنید. + دریافت رده‌های تصویر برای بازبینی به مشکل برخورد کرد. لطفا برای تلاش مجدد صفحه را رفرش کنید. هشداری پیدا نشد <u>ترجمه</u> زبان‌ها + لطفا زبانی که مایلید در آنها ترجمه‌های خود را ثبت کنید انتخاب کنید. ادامه لغو سعى دوباره @@ -305,11 +324,15 @@ جستجوی ویکی‌انبار جستجو جستجوهای اخیر: + کوئری‌های که اخیرا جستجو شده خطا هنگام بار کردن رده ها. + در هنگام بارگیری زیررده‌ها خطایی رخ داد. رسانه دسته بندی برگزیده بارگذاری‌شده با تلفن همراه + تصویر با موفقیت به %1$s در ویکی‌داده افزوده‌شد. + تلاش برای بروزرسانی موجودی ویکی‌دیتای مرتبط شکست خورد انتخاب به عنوان پس‌زمینه تصویر پس زمینه به طور موفقیت آمیز تنظیم شد! امتحان @@ -318,6 +341,7 @@ نتیجه یکی از دو گزینه را انتخاب کنید تا به سوال پاسخ دهید جلسه ورود به سیستم منقضی شد، لطفا دوباره وارد سیستم شوید. + کویز خود را با دوستان خود به اشتراک بگذارید. ادامه جواب درست جواب نادرست @@ -336,6 +360,7 @@ آمارها تشکر دریافت‌شد تصاویر برگزیده + تصاویر بر اساس «مکان‌های اطراف» سطح تصاویر بارگذاری شده تصاویر واگردانی نشده @@ -349,6 +374,9 @@ مشارکت‌ها در نزدیکی آگاه‌سازی‌ها + اعلان‌ها (بایگانی‌شده) + نمایش اعلان اطراف + هیچ‌ مکان نزدیکی به شما یافته نشد فهرست اجازه ذخیره گام %1$d از %2$d @@ -384,24 +412,55 @@ جستجوی این محدوده درخواست اجازه این را دیگر نپرس + کمپین‌های نمایش هنگام پردازش این تصویر خطایی رخ داد. لطفا دوباره سعی کنید! + دریافت توکن برای ویرایش + درخواست بررسی رده + بررسی رده درخواست شده + درخواست بررسی رده کار نکرد + افزودن پیام حذف‌شده به فایل انجام شد + به کاربر در صفحه بحثش خبر بده مطمئن نیستم ارسال تشکر: موفق + تلاش برای فرستادن تشکر شکست خورد %1$s ارسال تشکر: ناموفق ارسال تشکر ارسال تشکر در حال ارسال تشکر برای %1$s آیا این به درستی رده‌بندی شده‌است؟ + آیا این در محدوده قابل قبول است؟ + آیا مایلید که از مشارکت‌ کننده تشکر کنید؟ + عجب، این حتی رده‌بندی هم نشده! + این از محدوده خارج است زیرا که + این فایل ناقض حق تکثیر است به خاطر اینکه خوب به نظر می‌رسد + نه، این از محدوده خارج است خوب به نظر می‌رسد خوب به نظر می‌رسد بله، چرا که نه تصویر بعدی + تصاویر استاده نشده تصویر برگردانده نشد هیچ تصویری بارگذاری نشد + شما هیچ اعلان خوانده‌نشده‌ای ندارید + شما هیچ پیغام بایگانی شده‌ای ندارید نمایش بایگانی‌شده مشاهده خوانده نشده ها انتخاب تصویر برای بارگذاری لطفاً صبر کنید... + از عنوان/توضیحات پیشین استفاده کنید + نمونه تصاویری که برای بازگذاری مناسب نیستند + از این تصویر صرف نظر کن + مدیریت تگ‌های EXIF + تگ‌های موردنظر خود در EXIF را برای آپلود انتخاب کنید + پدیدآور + حق تکثیر + مکان + مدل دوربین + مدل لنز + شماره سریال + نرم‌افزار + اشتراک از طریق... + اطلاعات عکس diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d89c6241d8..d191a98ad1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -30,6 +30,7 @@ Apparence Général Donner son avis + Confidentialité Emplacement Commons @@ -527,6 +528,15 @@ Exemples d\'images à ne pas téléverser SAUTER CETTE IMAGE Échec du téléchargement ! Nous ne pouvons pas télécharger le fichier sans droit de stockage externe. + Gérer les balises EXIF + Sélectionner quelles balises EXIF à conserver dans les téléchargements + Auteur + Droits d’auteur + Emplacement + Modèle d’appareil photo + Modèle de lentille + Numéros de série + Logiciel Téléverser des photos vers Wikimédia Communs, sur votre téléphone Téléchargez l’application Communs : %1$s Partager l’application via… Informations de l’image diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 26b6bff4b2..1228946f35 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -469,6 +469,7 @@ Copia titolo/descrizione precedente Clicca per riusare il titolo e la descrizione dell\'immagine precedente e adattarli all\'immagine attuale. SALTA QUESTA IMMAGINE + Autore Condividi applicazione tramite... Informazioni sull\'immagine diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 260931378b..9eaf98ffa9 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -19,6 +19,7 @@ 보이기 일반 피드백 + 개인정보 위치 공용 @@ -452,6 +453,13 @@ 이전의 제목 및 설명 복사 이 이미지 건너뛰기 다운로드를 실패했습니다!! 외장 스토리지 권한 없이 파일을 다운로드할 수 없습니다. + EXIF 태그 관리 + 만든이 + 저작권 + 위치 + 카메라 모델 + 일련 번호 + 소프트웨어 앱 공유... 이미지 정보 diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index f2f4b39f43..27cfbeda88 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -10,6 +10,7 @@ Изглед Општи Мислења + Лични податоци Место Ризница @@ -507,6 +508,15 @@ Примери — Слики што не се за подигање ПРЕСКОКНИ ЈА ПОРАКАВА Преземањето не успеа!!! Не можеме да ја преземеме податотеката без дозвола од надворешен склад. + Раков. со EXIF-ознаки + Изберете кои EXIF-ознаки да се задржат во подигањата + Автор + Авторски права + Место + Модел на камерата + Модел на објективот + Сериски броеви + Програми Подигајте слики на Ризницата од телефон. Преземете го прилогот на Ризницата: %1$s Сподели преку... Инфо за сликата diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 5f73faab12..0df25a964c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -22,6 +22,7 @@ Aparência Geral Comentário + Privacidade Localização Commons @@ -519,6 +520,15 @@ Exemplos de imagens que não devem ser carregadas PULAR ESTA IMAGEM Falha no Download!!. Não podemos fazer o download do arquivo sem permissão de armazenamento externo. + Gerenciar etiquetas EXIF + Selecione quais etiquetas EXIF para manter nos carregados + Autor + Direitos autorais + Localização + Modelo da câmera + Modelo de lente + Números de série + Software Faça o carregamento de fotos para o Wikimedia Commons no seu telefone ou baixe o aplicativo Commons: %1$s Compartilhar aplicativo via... Informação da imagem diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index e2b44d87a7..4ec460d304 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -28,6 +28,7 @@ Внешний вид Общие Обратная связь + Конфиденциальность Местоположение Викисклад @@ -526,6 +527,15 @@ Примеры изображений, которые не следует загружать на Викисклад ПРОПУСТИТЬ ЭТО ИЗОБРАЖЕНИЕ Скачивание файла не удалось! Это невозможно выполнить без предоставленного разрешения по использованию внешнего носителя. + Работа с EXIF-тегами + Укажите, какие EXIF-теги следует сохранить при загрузке файлов + Автор + Авторские права + Местоположение + Модель камеры + Модель объектива + Серийный номер + Программное обеспечение Чтобы загружать фото на Викисклад (Wikimedia Commons), скачайте одноимённое приложение «Викисклад» (Commons): %1$s Поделиться приложением с помощью... Информация об изображении diff --git a/app/src/main/res/values-skr/strings.xml b/app/src/main/res/values-skr/strings.xml index baa3266b72..282cf7ca88 100644 --- a/app/src/main/res/values-skr/strings.xml +++ b/app/src/main/res/values-skr/strings.xml @@ -65,7 +65,7 @@ حالیہ ورتیاں ڳیاں ونکیاں ولدا کوشش کرو منسوخ - ڈاؤن لوڈ ، لہاوݨ + ڈاؤن لوڈ کرو ، لہاؤ پہلے طے تھیا لائسنس رات آلا مزاج گھاٹا تھیم ورتو diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index f77108521e..fe80d25209 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -15,6 +15,7 @@ Utseende Allmänt Återkoppling + Integritet Plats Commons @@ -512,6 +513,15 @@ Exempel på bilder att inte ladda upp HOPPA ÖVER DENNA BILD Nedladdning misslyckades!! Vi kan inte ladda ned filen utan behörighet för extern lagring. + Hantera EXIF-taggar + Välj vilka EXIF-taggar att behålla i uppladdningar + Skapare + Upphovsrätt + Plats + Kameramodell + Linsmodell + Serienummer + Programvara Ladda upp foton till Wikimedia Commons på din telefon Ladda ned Commons-appen: %1$s Dela appen via... Bildinfo diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5c9e0df05c..f03d043d0e 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -18,6 +18,7 @@ Зовнішній вигляд Загальні Зворотний зв\'язок + Конфіденційність Розташування Вікісховище @@ -526,6 +527,15 @@ Приклади зображень, які не слід завантажувати ПРОПУСТИТИ ЦЕ ЗОБРАЖЕННЯ Завантаження не вдалося. Ми не змогли завантажити файл без доступу до зовнішнього носія. + Робота з EXIF-тегами + Вкажіть, які EXIF-теги мають бути збережені при завантаженні файлів + Автор + Авторські права + Місцезнаходження + Модель камери + Модель об\'єктиву + Серійний номер + Програмне забезпечення Вивантажуйте фото у Вікісховище зі свого телефона. Завантажте застосунок: %1$s Поділитися програмкою через… Інформація про зображення diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 60b483018f..66b74b66d6 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -21,6 +21,7 @@ 外觀 一般 意見回饋 + 隱私 位置 維基共享資源 @@ -516,6 +517,15 @@ 未上傳範例圖片 忽略此圖片 下載失敗!因為缺少外部存儲裝置權限緣故,我們無法下載檔案。 + 管理 EXIF 標籤 + 選擇要保持上傳的 EXIF 標籤 + 作者 + 版權 + 位置 + 相機模型 + 透鏡模型 + 序號 + 軟體 在您的手機上更新照片到維基共享資源,下載共享資源應用程式:%1$s 分享應用程式透過… 圖片資訊 diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 3cfe500d49..ca389d5b32 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -14,4 +14,25 @@ @string/license_pref_cc_by_sa_3_0 @string/license_pref_cc_by_sa_4_0 + + + + @string/exif_tag_name_author + @string/exif_tag_name_copyright + @string/exif_tag_name_location + @string/exif_tag_name_cameraModel + @string/exif_tag_name_lensModel + @string/exif_tag_name_serialNumbers + @string/exif_tag_name_software + + + @string/exif_tag_author + @string/exif_tag_copyright + @string/exif_tag_location + @string/exif_tag_cameraModel + @string/exif_tag_lensModel + @string/exif_tag_serialNumbers + @string/exif_tag_software + + \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml index a2bb6860d2..60d0b566a7 100644 --- a/app/src/main/res/values/keys.xml +++ b/app/src/main/res/values/keys.xml @@ -5,4 +5,13 @@ CC BY-SA 3.0 CC BY 4.0 CC BY-SA 4.0 + + Author + Copyright + Location + Camera Model + Lens Model + Serial Numbers + Software + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8288e3ab0..9bf5418612 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,6 +5,7 @@ Appearance General Feedback + Privacy Location Commons @@ -165,6 +166,7 @@ Requesting Storage Permission Required permission: Read external storage. App cannot access your gallery without this. Required permission: Write external storage. App cannot access your camera/gallery without this. + Requesting Location Permission Optional permission: Get current location for category suggestions OK Nearby Places @@ -536,10 +538,22 @@ Upload your first media by tapping on the add button. Examples of good images to upload to Commons Examples of images not to upload SKIP THIS IMAGE + Download Failed!!. We cannot download the file without external storage permission. + + Manage EXIF Tags + Select which EXIF tags to keep in uploads + + Author + Copyright + Location + Camera Model + Lens Model + Serial Numbers + Software + Upload photos to Wikimedia Commons on your phone Download the Commons app: %1$s Share app via... Image Info - Download Failed!!. We cannot download the file without external storage permission. No Categories found Cancelled Upload There is no data for previous image\'s title or description diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 21df4c045d..9ab4d5f5f7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -59,6 +59,18 @@ + + + + + + diff --git a/app/src/test/data/exif_redact_sample.jpg b/app/src/test/data/exif_redact_sample.jpg new file mode 100644 index 0000000000000000000000000000000000000000..177e42aadc4e5516dfb771543e09926dc5bda9cd GIT binary patch literal 164189 zcmbTdcUV))_b;3Rq4&^>bO=pKKtc^Yp_4)rm8OsgN|Poky~KcoCIqAgLMRFd0;1=D zbP$A44pKZw6%`f1g6-URe!uVU-sgSpAMg9l^UTcJv)9b5&&-~-*6e?l{&@*Xu!|>L z003NE02%-QfD^z86a;`+lq}0%0s;VRte6#n`+>m!(Ch%9{=Ym>*1MZPVF37F9u`*? z;XgcphHRDp(7;DPfq(fqSbSA153srv9CeuxERVHyk`KH>2#b-wa5+jo-q`4z5geee ztA{Yrg`4OhBErBeTgjwvYkw+1%EDFmQ^ z@_*3z{|C+eKm17rT>mXKtFUaf|E7ZhfPA)p`B}(->G|OQp-Zd5|3SkJpn?B)-fGZ) z(!u=&p#Leqr2q)v{%<)u^8tY0$k}xD^>tY>%K;@=K`@}SB-xZY!6HSnSuOnP{|5a3 zmd45t`!_#8^6+;)R_VZhB?Y;%l3CR6npu4U5@UtG`x4~!JDu%+(sclk|40h5XXXCg zqQCiFS-t+7pNB=WzKMUG3M*vwEI{(VVvZZEnAJ{z#Q%E!PdR`O|DFEdy#o;Xf4tZH z{rtbt0RCAA2s+xM?E$Rsid6wC_-6%R0{{VkyWb(`w_^kU4%yk*z-$n92;^VS!Nm#T z;NpNlIC(g^xPLnq#mmFZ`}^T{A^(*X#100tb8|pA{$=_96#UZ<5aMJ9fcwEfMF2<$ z2o?hVGXyxnl9T;k@qV|*zYYijv#~=sSYq(77`j*^2M7kUgl1<+%;LVrdJkX|Vi#7> zvx10t1aK%uLiJN9HJnP;?E|8i*$>JFfl;Yk++yMql2R(FYU(fzxS`Q0V}yx~t)0CC z3hju+;XS>)eSCw0&tC`$B@m-yuEfU0CnTn&XJlq&=a32ti;7EbmzGg$>uB{2^hQQg z$NkQ(2i-lr51$MU4UdeDjZZM=<`)+KT6(_xa$|FAduR96-s^*phew}2fBAa+?KdtU z0Q_IDSmFN_mkcxA4|SW*A6ZzfKDQqQ*5z}pnP40hs+Kbr1&j+wDgHG7YpGf1qSE&OR#~jJEdHy zS&AiuQwrlhnv5vf8i_Z)y%rC_BO2gCUD}^(!BVsO`ioa?rs=6IcIx*eyABkf5M$=ifI#B8J`#VmB5f72Xi;~WcC{>IwJ?K$2?4t=N(kcP~Os|+bMRe zOG(@%Ea}LPCMHb3fE6_j6FBuJq6;xr22otJRdNrA3awe?Ly1Y~?i$MWYXbcVe;a{)?Xo{##H70&8; zmgqhxa&)~pXUAfZE(f%nqe3Si)(;!kq;qjMI#=C6in$nH8P7FSefCS zYUoFmb>))X`{j+LSgkBE#j^9@#_+{Bq?HVIX1yxWPjfqQPDtvZ?oMTfm+~*@v-<_; z6ZO23LGn~<&2vwMjR2n-LKoLFi~o4E;B@SZ9#W4k^!1dt;p8=02ut10o)3|2G-lf< z-wB_7abzD|K`jS4$5Z;4;^ycEG5LHx!)~F))RLhCiIw-fb5l*mDjF>+MR!J89AL71 zhSi$-Ty+IXn~2F33B=%g%l(%$T2Akj^UPrHcEdAU39&DO^dHWk10mey zZXS-!kM2BXq}^7It(#JJg%!0Z*UztG1`z=AjIuCUia>^~GdbFc;sCkCxf%(T(gnMs zy1*6(b)fqka5)M^bo;_Z^%lec(9s0NNjl>l1 zswg8He8Pj#DFo;A+a2ngoj(lh(mNFs*din)j#_e`yo=hZ2qE<~ISYK#KXFOJRFjum z3%vglHPh~~k5Sr0{MdUhwlHih`X!MB40V&;Tur^#ptv19ge;#{k#y7xC8| zljLx6o&9;!;|=+%Pt8sK0aOKe-Y)Ex;qLrUt5$r1*y2HHW7k|2Y#r-|t_nGx<|+J& z3k+UMbJjA}#ghE0d@qS+L05Gp_L15SsRA|*#IPGNKM5$dz+@=zQFU@6Q=~IO9U~F! z4oc>5yf~k#Z%;fW&+86Jd`hpw9gJ~*Uktc_9iWfKzT!`4ZWF(m9|ar1l3GP4P+U`H zemc4dbbmE>nJB{DmPg#dQNWq`l;XecDK>2Sl2`J)=M8$6akyP+)D*5Nm6)zdr9Ta; z{8Zgqv@%bsqk~=(L)R!{s`+Ay{&c=?^MC3X8+!LlN4p(5orKh%A>TOo1>ZovCZx2T zl8WjusU$SFIc_M|d#5=bt{*rn%`n3PO%8Elq218eZ!qU$cMYw0hAZZ0&(!78=G(=D z`dW29Sb?h101S|pXERu$p!^M3#tMyjg<@ONG{bym$+Wa{f)5%7a@F1iJYR6>M+ zui#s!ZSVy3(xNXe@Ua^do?LOI8s&D1_#pwR{o`d}91~@(6!%)Kl8PB~+is7I+ir8TI-RR{kc} zM^aHR*-)$}Q$dLukBOQfB-IBLH!w7YLP&&UO``LRAcL8-E{xd<`r_>>lj z?vyxm9c({QRuH*d(_6%jU=$l=25My1>o*CS&g4IkzyIL7%O?6&gr?iXxrgjt%%V}>y`BCwU2Vcz>isf&$S|7^0G zo_kOmp1hOqJYihT^)k?)5R=KrWQ^(sjWaestsz5QQpmWIzyQiHN=Ag_N8W^Uh1zF( z=n_qH*gJW*4n1dWLJSLAUFFW*r*DLUTL1W7_Ox|%v9%-nFNvlS!KOn`mEz3U88HsO<(JOty|+ipFl)f?xoyY?&Y}Lx zt>evl`W^n%yGNAvY2bUL*atl_*4MI76F)V+)+W^@BA^L=tQJ|TEKnd#Rk>n|U_4>B zUTRs{J2OtQXh?VIQjEDep3SihX?@aCHwh-ezp@Ga*euYP7BLGf{e&Xi)Y~*~f_6Wz zdV_U-+88^hy=5+OJqCoj7{Jd(x7K&%Gk@tNgY$dB&Ds!D3v-Qqud6pw<>cgmhKE!) zaGWjTq$1qx89})7W0u7jf%|`oURWEj>6rm4XmFh-fOe!2Qwk7`ov+euri!A739;xW zR8J{skrFjXQEj^^BRf|@*}WqfH67~M->F!JRIQHJkK*$_fAS5gNv!l7hRwkhw)HZK zFhyp~p@{^)%1*$7mi+>~Q7!LiW}+8>k(4%b7dwH{HHJ10U~9tIkz?-FBb zDw5HgqV@@AeT?oo3io(73_Czdpq^?y4aP6@)w{K5JDo!6IW{uxuU;eI>v6oZ1@?RZ zBiMrLv$Q!*Ru;rd3##q-7f`qzp)xu7gb3jPxmp`6sOr_VhpLBTKxQU zy?aM2r#&&AK7#+#m(7>V1tlN4=5T`x-DxQaQ;zo`d#KRJ8@DBCXzo<529dUkif`E3 z{1ZjLphKvFwk@foD2B4olwGeq4lD6i`TIYBT<{+mB|l#d+kX%pKT)q}Aszw$`W~88 zzzv!h1X4I!Enu1a1izupEpsA>EM2diekS!LPFTRP&T!=WpTt65iWo=t*9FbEsY!#F zDY&V0ti75|A1Qxy?X=-p;Yurwgj!8b(b>Vj7_Dhpcp)y zDr3updNhIq{wc>>#AWGKJpF=Hp3ZAM+c1f(xtUCxo@f`LTQ3!FawfQYQHBBR5>Wc~ ztwI-mfwM3u^|gWy?O+&gP&2WRT_z*g@BZN~zdDZ}dBP}oLGt`bi`6g9BPZ8nJ~x$o z9&Wf`(a8b3@OOqhmeht&t%9ERN^wa_jp8%)*5V$vlIyHI`_9fFVVW54V}0lER?Y$u zIpF68So}xafoGX$))>+9enEtK4{!EIH2hFqRMOLagdi&|0R?KTLJ_{jmw4`-$i>Mw zMI|(}>`SiCK#CGz1qpObQMm;S&w>)$FGKyw$0$hdgzH%GPDVaMvHe~CRJh_QXr!z9FAOy#^b|qzUqQ#hud8_Q-(_+p>P1ls&T_^s4_0! z=1=%Negi6?rHd!uK%y6t9D79rO8n9?rXl24lRzQrVzHk2Y!sv|#=^3Y_h>o-~f7Qbxs^fel)g8#xMFt5!rv&a9a@<+HQw0e6= zh)m`c_cLOQbO4P?`Y_AId(JmCW#-v2y2u95sA5O{f^-HO#hKSh&`w?QJ#wyX{UvL( z@bxKsxLgO~hXLto%5##R58Q65A)qadR`Mjr4S8kmq@dK1(u1~fFr4c_Zs)|~S&c37 z1}V&?7y%!@fX6hgQrX-2-X5zO!b?2AkB7)$*OO7`%hN5{)(SlR)xwA)6?;B3k4=Fz zsTq1aMx>BT?$D{sC#r#v*awkJYPYoO;!d+NxdjP-uPfK(XaYRHZ7=DobbiXqqpya} zH~AOU6X~5VZas;+pw*vt#2DJf9pbm{#6?>#akbjdPdV!jDwYY{-VVm4No>|DU1blr#UgB3p2QgTZAnHU0{LY z(508Scaxf&gK^EVmm2>xG*vY&$$FZe!mKk;n9yZlY&ERsRRp)BJT+^in?BYQvd`m#rw znbUo+Rr%?b%sJvCusNdjTqWBR{$g~fKGPo*?Hm%UJa6RHnmrN*7bd&}@nGw%K*cUl{XRS)|tZS6t3V4R>b7 zc%Sa4yy(Bb2P#t~7*Q!c_;7NJ#kC-pr4iU`zpYz~`OKNN7A|;8 zI(Z%;9}>6gCeH2v<-{0$Y3vyvHurNBFqF2(74U(HgT+r$x#(I0MR$oOVA<0|?+v!d z#T3Q9`=%79?bP^YK9Gip`-rG_Y67hbU>&rMX-Ua}6iEz5S=Ho=j6(Y($xvmf`uGT= zlUPq$;dpwx84$N$zOP@#xgIkGevtO|Em=?eQUi^5vfnTe0afL!d;3OS*`57LkN@=m zPbG?3$AU<|cq82wz{U+!v>UwZqWYvx)4KO8px$MwVup+FbSBaUJImJmur|Kb(j4~d z!hHWeQm;d;gmc)IXN*&p5nOr%x3*3rv1weg(?mULXFEVE6-zeGPCJP(3l;>T5|q8` zw7vhjKWX-dv+WkO>uM4}L*S*`oYov<$7+0!deKVQyZ?dbl4X1zVcg*MlV9+>6KW*f z3ino}3piGl@2|*)aL+BNT$=XGoQ!K<5KpEX&7vPQCP0yf4Jiu#|$B7=D}!eZN>Ck!+thhpQx8N}XMr0xeY3 zjl>COmKa@mnYHaKEPOoWnu3yF9J9HON6>T-!&*5bb~e2!W;3lbJc5O3RD2PoE7wM* zmjh0oB9pY(_H#3v5R*YK>UEMH;kezX9LMinO;QM4_%v$%#^x;}1Zgi~Z;Ly2*1j1-hspd^#EZ4E{1xvff{tjjhDS zI+fq@St3dJB^pBW=`+-}&%sA=HDvm&&&mvTDJP$L$}SFa={YkIiYCd(eUhN0?oXL1 z_(z0xw0Ym>T|!JbJw$LAB2jw-H9F@Q2q=;3x%eN`+$C7|{UtXETHY)6E#{>9$H|cH zugo?@#tr?=!U}xywlSo6#a2~?HM^n(VdOd49EFOStpkkK_gkWX++tT))3&Psv?RTi zXI$8kZcc=0`{*amg}-|sm(UD4UlvJm{R^fN*fipEpGpx-zTt}%Y4AyCP#Iv$f<6$^ z09EglWIuO9Orf)vWTcSUGzn{~J^YitkjhV{5qfXXa;xjVp!|b4RP&r-^O|$5L8Yc$ zEN8hyOE^~54VGB=3(geH>5L!HHi&6uzpN{$hu3x=xY)^0M5&J8X71$Y80qlC#H{In zxuU!AyYNk6kqV|%57&T1B1ImpG;zw%6R%i+pm}_(I-D}MFAJ!0Vf?a<4f%+;#Z7=yM^-t@%Nv$5zsZ9W;K2v!N%dcLI$e zw9KgHDeHHvV}staM0lHOff_;d8@0dyi0$jbB!Kh=Y7t`yc&KSD`j6v*jgqx=WV>uA zpG+$C48#kbj#kRTlA1MAm~^`f*6j8Z2IJMD&(9PROH{MR{T+Nu>>T_q)Px~hhCZ-! zdMeKA{CdWf>!!x6Zp-*^&Ww69;ZdK~^;d@UZ~$sDuyRaTJaXkh#a+=z50sB9yN2vw z<$UT^mc%%nO{Z82U4Z+{n7?&0t-snt=_7vXQ3nlP9t4kz?;ojWCU`{Y*d_=Hi=kvw+I&a}&QhC9h3~Cy@f$J)2#oCr%zUsG1a773Qur0*&@3yI;+Z2VR|PbrNmm@@tY z_C{53AIiBgAHFUXQJk_hPtZkJ{61Fskygv|&>gx74S$xberZ5UV!YrKSfGuRXt#}* zIGa=fSFx232ebTRP&k|{KP$nNQHZ3tsr^grTmHcZ##bZX{eX(nVg) zUjEANV^rbsHFi&qi1kBP2YLVbl=GK_tx}xsM~wX#_VUEekqS=5AFm;Ksp;cM zImQPl96y{>#i3RR)!a@e%Oh}=pJLgTjm_}VS3HaJt}cA258R|Sux(1L&#|=Upr%p8 zxuW?T<6jCG_C{4Q1N(xugc?IMktQpR>GgwZ()W=fV$a_=-j=q9wL2Gp?!uCCR~Z|{ z$;EPNqFP$ekHm5%`dQcvYP~YFP3(--)*ME!FAepPe5JP?h#ATu^aByAQ)Qx51tXwG>ejY9%507;FY6xktxDUmK0h~8LqR5jXjgg4SrAt0sYa;Q zf^#^G!T$lk#g<)k-@k)-fI%+GoSg->X^ob?cHUcRp^dg-kEhV$WBPU$X@8_BU!vy~ z&3u*lB&s>xqx9oTqjHHp4g4#iq03Xa{cPBni`#jk5#`s_Ir*V`-7^crxBN|EVHW8p zMeLWL;tCNChFZ@3cQ$IvcQ1@4T6IYG#U^RN5}6>67k=oHQH^kOI>|4b~0 z68Vhv)Fg-H=OqHNZJa0H?Ns*o9a2TpVwRGlrivu!o#Lb? zPik#`+I)f{xwWuYWFw7QYgU9_oUAiAk~Kg+6d{zV=%yR5XoR}dYwH(r&1`18G{}pUt?8GK=u3aJ- zn3~Xl=1!ru<;fLqX!Q=OR4XQ1_YNZ}_zf_RWaJ{9y&?K8y7OeQAX1`$ic|MX6u|xs z{tC|qw04Fq?o0i%LUEO*i_$TjS!yMQ=Uh9rMqWoAO`X2LnnNs(Kv2Yc6!=6rRMlXd zo+Bk}p<2?f3$*dIoPtOM}z!9S&glw)>w( ziOjaSc2nIq=!!#X+SORPGL1j9?KmfxVsEumlYtvU)uO?1w66X@7rihyS6oD23AM%G zBlXNX6Ddel=8yq#Huj=ce^i41`TuG1kp6_8s@PF;OCuChPaNa}6%xqk)9u>W$8YT)~0lo^1vWavI;-=LF zV?q4UQ>u~%nHvQX33CKu`pi1Uu2Ty%8(dFasc+4^*_hK) zQd7+)MqW7ADOWw8@0YJX?=2-KhBLc0(vTTttXhDW9gxB3*X&vQf@bc^J`rl)yZ-bp zkL`lpO*(=h|L}_Bn$!H%LO4eLc+@dC%+V`>R z%lKau57!+-<9`6=M9*ZS1heP1j)~OSba(_ixa<5L(k151D5xvxlV9H7j@s2K5w!8O z6sKO)stIODbU>Bjs}KZcoBQ=t7R5o$spr&%9XJms&R%0HlRiBusnpomPvIJ9rc7Ml zgPKJe2vta3vkM*z>d`XlVJHsDbxKE*TWz^KHJO=GB?jNHX>|&#dhSD8Ay?2{XN=*g zTUr9zeYgGrobkM%T)|R?bP;1w8=93wS}1d}4eR6~7G^)BbK{wBm&~IO;|487!%b#k zgs+a?&iPk_2J=Bpj}ou2x!DgU8~JJh^{9sX`F%Lt6FW7Is(O8wo6OSeN!>TjBC=oM z>o(o8uu1N2;~I;!HDOyQL;i?{9~8d9?WQo3L?=ZkI6Nirg;}cq9gnHK^>U}?0 zwS~z^kUw-NmDq zI|k=*;y-Wo@t&b1NN=qE%)pnBJ*6_&{I4psdg^2=;vJ1#x(1}d%LdI5cCgN$Eg$Q4S;@8d8+Ms508ZEi1}^P zXA|n60EH_Jm!SY_IikLw6lkbfVH%3d_D*$s=`otyP}$m*P;DZhcPD*b1l3lfW#{-s zS}--fsZ=~lrc#|h)p|-v%re7=eC&VAH`s$S)4)x)+nv=Q3LSdP{Ex9DMRqG~)>u%M z0wiI>kJ(Y}brrj!KHiKm`Y3FTJj33Jvr`h4DbPIc06&@>T`WCdhXS_;w%r~Q<9C5^ z#xx**v`46YJ$@ym;hgo@NjlYJA=qzMCUL%9t~RBW>uEKJ&xFHI z?zm}tgxT4yHrack^2nPYVl)%Qu5o;c^A%C$f{eL2ASjg(qp?M~?~pGIKcbvA&9HZW zyS`I)cb>(|LNkZhp)p(vGCOI6C&iJ>xJtFbzewrR?zsTjOb_uUksEF%@47y6g-g=EQX|0+k+;K)!W{Qaql3q`t0 zJ12?(=9frmmHZoUVZ37J$3F;y=SFSeSA%w zhiAK37Dn5k3pd~8v!CElOqL=?<`a^BR+E{SFnf+GVA~m|l*NYcXxT)(-ElmGhCT(u zz`TVnPoY^he3_6c$Ceh+YkPY!lf%-fS^;%3QZc7aU;pX(9TnYK*=3SF5k*k(_Z9hB zBdObp>*COqGbgB6csf#>KliHk2z4t`XSB}E1%9m7xymwo9)4=(j^kx2+#2WP*M)5u zJ^$#wFx1Jnduh1<;?HS##wSfM+Reulj4`}K`|2Q}O5P6xGnZM*AY98ONd|uJg zUE8wpdO~Vl_7f+0^I;SD1|yGV28Q8 z!M(*AML|dJkww2Jwe|fe`@Yf$n7me9MdUHg3;3666;IeYbBqfCompsQ@H#F{;?LM` zty(_MKX}pl;^E4Q!DdCI&vMdbc&=vJ_o8rxUWXLVfr{Z z;Ab~{^Ixtos;!qMsJA6JWMAtSo>Z!rcYSi)QpZ76%r}Zef!YT#kDR1rvs!`8Y;XSh zap+J|rv@LJ&6z^k#6ShCj)Jzv~-;bKn0QL|7@pl=oONY!U#6-Kv_L|iaW#7EV0 zh$>@CXWqe6h&j&TX%7lBj{QOmrOtM+)|y|$@x|m<%GnuQSp1K)KI3K}VvDKw%S_-7EI(% z7`bJVnV&AAEA2_`-zf}orw1iHNfsPcFq4XK<-ft5q-=zO3#GamSI5Des}L-+M>wPc ztEQM5VMGFLyu%LbWqonsl+0nnF2s`EEz$zk(x?gYyx-hOz>&mn^VrEgy-_PA)uN}4 zjT==L!#td**T~@JR$s6hAaXfb^N=i6VuU->JI;_>t-R@etSvcxbq2g%(>Njc_jHO__$B4D2CS=`Kl(Qdakc~EJc}i! z9j+1^DJxEuankvTbEO%Qs@34sv7hR~o!Pk0cgrP3MsSDJGHCeTuY{)j{J+(WmHq+T z!`57p`_mEGDf*EPD%`o1PiXMygH#54`d#^kMOrBjf8_J&%%`!tbz#fHLJ-Y@2FKi`) z7IV2pt}h_W`xcZ-%QJT$`}MAD-Il>?;$NUg6N4X5nZ{v-)Sy{1oAp+mwPY!ZvS|VX zRcjkfX3(|XRSkPURth+vb^M)QKtpkRKOE?ZZTE8^Zc2;yf27(wDAM!nGIU4ktF>E5 zWr&8Z7xuIGA3vY-xVU?7&HBrn-a(kf(ocd-dn|t@+LJfG&7ZE+w+}rH4B*6OSmrwO zze1~3H_f6)_{2p@1C{=&msdLWwVr(A**dblOl+-EiWH4CB9plj0jX9JN|txbRVHNS z!?_0J4?GI+5th$d9*b3?lg)C3D?(-u%EBj|cuo0Cq$z3wu7wwGG!ql^ zmbu{BkiQ*6mhrd!MWyXv%2JLN1|k43Iw8YpO}Ou zUvG_)9UJv-#~=Fhn-*R~Dx@_E=@3MXS_SB?X>=F_ZyhX`$LSAp?H0N&rydHwUO%&% zpgAT>3{}7#BlmD`t0TWp@7!Ct)2o`bBr+2hK~+?~bGiJZJFO3XQl;kvJ#)sU6c)v&m#$uY<&CRe1OFVAEcS{|$>1KM zcHnwiY{vssL$3z<3u8mRCgg_(BJc%`K~sKf8?3yY$BxbKdb)!9QT^+R`M|PSz1jSO zSVzkCx5^{-w}uk-$B__KA8|y!k^M3+ek&Z9z)#duLoA?x- zS9Wd+jhZ(}Zh4CrJ|hBrQaEixsHA11XV2a&mdr?&W+IwTD|vLfiwUW)y)+(n5`X%Y z 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°b=P9AKv_EOcj|T#tuM!eJVdJtMg4uw~U{(TDT*tCnB6L z?c_KQk7gL6%>{)Z5sGL)tAHut(Ytr*Ii*;`46BNfS&)WhI0TPzPZGEXkbbn{j=ffu zLJ{+!rjUr%SR_)jM~Y#ziLzjjim@HM4oT}nSUTj#(t-3Tm>o$DbMH}*Rfs@p>4sGt z0BN{P5hPU5Eefb+azIm3#+wEujAPcM5TcUENTH){pyrj>T#aLmn5!uE_NP69V~52% zY7z6tK}g8LITTzGS?weo5s^qO=1=ut^XpdE&I^K6(|&LdVNEd9rqO(d&x|+WRix51 z+mLer9=ukBkugHS^O~%J=?NP%_*Etd%|0KN7cZx3GDb&j*&nSh+WbvqxRFxedgBJP znCGl#J?YW3Xb3xpvF3|~jD+zl2zF`~62yPKw<51vYC1*Q{{W-jA@(iJPbIWB3RTfh z;Z!bk4L)Wg?BF-37^P$@XjzilCLC=ewN`sKM*Y*UA6lr|UZrNlSl_rEgB*|6m340r zS{=7mNiu=ecr;jPmg0N3@}~|d=uxReAf3!!_YMr zb_6RVcq*fcj9ZbCr}L`|8vg(hqwMT*Rp}-P*rG$^tA13fdm?IoWaA zt(~~0gamSbDtBlVir?whxnOsXV@`)zbU2Y&54fq`7TbW|okn9W3b-74(?T4&rM=I{ z#YpaPPAP2Qh~JN`RDC8H31z~cRW%yeT|!F3cb{gZWCfE9(dTH$rg_S|I3SNvQLWCS zZJ#;c1ObnFH39M4kT)cl2A9mnP>hO*LU2fsap_G#AUS5Gu+a4g z)y z%%rj&2cf*FFz;pG*w)N+&_xYni;#b!|P$ zUfD>59&)@_&>8Y_rCDmd#fF!Bg>P(Qmp|TG)-~GPhXd#T05BCer$;&4wDK$0wU30} z64gUXZyC39-FjCktNa(%Z1+p0-7LyF0y(W?RuUz}ql_awgNkm&&m-wqH6IS^`cn~o zr^uimyasBCErI~fI`dj?NHEDEPf9$)jg8uoaz^viQn3uAfk12~QPailu z&;qJ+xliFp0m&}EOwy|Pi%#0W;6EOQf#@Vm0vsFM19*wwQE_zp>TiCTB&0Z`^TSu z&a~jR`9lxJplE%%08c#Bviyu6)2%&V3NxOx=8MdUzMNDxSoj|-O7tS6lXBy4<5AhJ z=!dAQj)-F2ik3cy*`^CzXK5UD6xOwghGTsCR3g_>wg<|&3^CExlw96MzrM2EJy6%2 zSs=B#RmyH3UX@mVw8lu00TnXp!3GuCJ(yyj73xdOfnJS3&2~a@Da=MTh5$Y*E$8{eYcoggeAg~xH zY4Ogu##nu6c3dYWGf0wxe|Z)^53L#ud~bkRWc_F_yOueiBn~i6-YKTs5A|~A(t!<` zV<3_b(29j2iLy|f)a@LhNRd^CsHO#gf;k7b6eXY$`Em}KrjqP1N&IS5jzBTy1534s zP*2oS8CXUJaC1=00!RpWLHs_oY=vedN6)P{0QD5sgIBShx@;pRGRywyr29R<$V;62 zFs%a`atD6&&m~I`4@!5`F3M?fcLCcpWrwJ#R`LdHboz>s;$jFN=dCsx!^e(s8;uvGg>n1i1Mm1W*Mn zpy!-oqsJs3m3DLr4)px0KeNLk6C8s+hJYXmx9+(V@0V%a%^`wxBoJ{;W^_0|prXx+ z!vTyp85Hbre6gBAD`y-x;Z6uxZc~abJCW~;J7k1|>xv#284kvznctn*$E^zSs&JtD z(?N2(!032VMMUCdKO&m8BypcD!5*2W7c6;7Y20g4nF>Z&b4m$3U=^v}A%YhmpK5Br zae|}rp(_GNttGXrk(6ZX>MD!)dcsyLid6PwI5nzLNZTL&s$`Kq(#?!}Q@cT9E8PaBH2Z;ujD2xX9}B`V_HNOz?v09Sz)vQe%Ea?fy{o9o^Gx#mO<)L) z@Jt6qm2>*ie{bGIHtHH94_Ace`O#sfz{M!)4nCBaILI|UzN4hwjj4CFzqug6r?#B3 z0@091`zJK7W*0?qASv^V{V4%Kk+T%zXC!3seze_#aT~esnq%BU5g#;WgpyJ6$xc+~ zkbbngLk#0ImZA_cn?0z}?)WDv%ivNi;xf^k5#FU)KYwrVM&k~aIT`A`-RMKTge{HXom z$x)np(Y?YPxgX@x266Lo_)r1|la<;VT`$jTEXK)^e>r$Bs$ydPiH1u(^D8QO`4Tv97!tA=X=e|WXQr2}X8}0V%R<~qf zz&^xOQZ0*p_%MA}iV!VI#@=Kn%541&G>H*Un15Q7#m+76Ptk=&%W()IXh|R5Lq&lN zjGd%zrCCB{cIJ^<0)FuyQHpekp-$GwphA3(3v~L>$TneQ{V9tZk)7ET(pe5ynxGL0 zRv@l@>PX}Wc|r9Q)sb88j8IHOee+9DB^OFEG5OS9bWBe;{3)yxs=jHCs76UNA(FZ# z!s#IC=~cOMcOI1es)$PFn5@m5f<5Vo5xk@O$LrdbNgaN8-#_Hh`=j^yH5o#L_7ni) zD3Nx{`cn<0Y^Q6}fk#7?$E7eS$x`_Apa|OCm+roPwG*3!1g`?7G0F+SVUkK9#fu=A8P4LF-V;O`cw@9V1PKQjIFS(bI;gt$Qw|;lB>+0Ilwq9#2+5gIw;d@QY5l%V_uBTXsk3S}L)7lgQ6H zj~P2h)`gK(i0CVz)xI2fpGgKqvh!4RXuH-W#hljaR(M)A^mWBrY3xm!8|8-^xFVzS zTc4YO(vTO6q59L6-G)yT#3F81$_jX6o}XGcVTBZn<^p@;u4n;rq@Qt$Y;iIJwX;tu zrFwHsWXV2JAkZ}I8;I^ts5Fj|w^7@Qa_$3j52&PItfZ$}0>oZIFd+}&QHyhqwP92u zARPw-} z=N)^Bg<^7W2TG&_F2pVmT87{(u^8BJMOl&uT$9NE0QJ-^gf2Gc6-bL{XX%8;6GM-> za7|o7cmk;z3>tw#FdGDapYztVAj7sX(wh`^v0~j>PAbeQ194DVs~0`%Lf!;m7@mf+ zG)U0XV=kDjdsw$A3_5yM9X=dKl^&I=WEHZcb5P5Q1W_bpj?_S;BP)F>SRzp)MtBtM zst(+9P|E05wha{R8TG62A$ZTVRJm431b|ih7hG*3rLafZ4!BH-0roW;!j3*}D!{S0 zEB688nqi5M6=>_vu~#bnmNS(u57bm`ro)1*ZoKzS4O|F-V{hO^Ibg`jF%&HZW*ss} z$ND|gi|pex7&R465hcg>P7P|u6AbNOezeIUaCwW5RW!wOBwaPTVn`&6_as$_btEgq z;l8zKTOt1U(Kmm?? zaNzxF783$@L7(?(^i6Lnkl5p<4JIQeYJRkU&+|eO84fB)$&`GD@HK4O%ESF$e+p-o zk^HhiSKLqqW{zNUvic#dKJ94DYj|Iv zAoiRDZidz9) zpwp&Y1TnGhq>69$b?hL$mZBT|ma8fvDpqL!01zn_(kJ<#b4@VVqxS7q1}k-ExO%NG z_N|W7=0;Teio8wKAdVP&jMZsPthrm8U_HR6r%)Lb&|Kufc0P?xmW)g#K!ZWw!0Qu^JPq?@BY z;}j&&SVxQ~!D^4mm~y7A387@d$glqO1Vtj@A|U?&uyabmW>h3De8`X26pW#lDBnuA z<$Q$h1dha2W|s_9#wZZLk}&!6NE%4N86y=uZoc%UC6FDU8h08h8Hqfo`qB{i>-tpB zZ?K<~k9<@u6vHJSG$sOhD8nT{H5Zc0kf=SXK_m5WMLBjzIgoMQn8-p(qd%afM`MG? z`c%B*X(7Eiry?;pWT09L6~p6e4b-v-q*ARH=||bm@G&1+catKa$tT{J6(2{jxXw^# z9+gz+(!nNd?ioIW)|x^_NRtQ9(jT-YK>>%YE3_jpzPWFl?UWDi4u3jhYB3-9{L|SF zc3wqmD=rV1gnD9_*4A@m79938?tsp=y0Ddqis^Ix);OyM2{Diqk7{MlhixXw@e3r)Dvwyu`@sP1vq5S3b2b+2FVY!FY$;IHdao>bWB$){xbRqJe=sT#v0w5xGSm zeAF9xQ8-+Wts<<0kfcy9f)_$anh(fRJiC5Kd}py0DR~%jcpZqQG6w_&{HOtqVZ+F*NeKvKS0g z1Tm;BgY*=F!x#o~WI#vKki~B3e6zNf$=u&7GM{rwZJj=4IjT%8Vi@hNa!O1XQ)FWU z5j4Hh|jDje*5Q~dxGOJL@Zz__?#QO@i$!N-2TYsCm98`Wn75@N6 zxFGsCri52X8Ar~T{3zW20K?5ow&^pzAmD#@nubY{i9)+ixyF|fIu0h6lFu!o`6U;aw9a)HPjH`kx_}94WJ59 zbGITxO)CHbxyZ_oY8quD1cv(4L!_A_x1~q582Wu_*az*5wl-tb(N#Z_J1T&*@F`Ll2Y^K9p<+DXIJ> z@a5j=H0y`9bI=}@&T4-QJWHo5JQ1bJ4uuX4dT3~mB{}uYMnb+!Z_lx-RV0aUi#%fD z#`eIGd1WL-b>5s)A}GUlbM&udy3^vlkITHbj!gDt8LSOY;m?O}3rQ8TK>q;VLs~|v zdy~k~=WL3~cITRp%V0=Xc9W?77U1`;cTm!`jX8?y8f1x&;tP>i z87H$M%@x-qeB+vHC>g<}jnsp%^zBQzdLH#b&|=2rN|w*2X{CxU13%=`!ZMOozLXr} za9rc907dt*0P93n_yu3LN|-Ei0(NxoO)xkZ2aePTg^L9v83K`Ac44ue%}4Ug1+`l-5010N@#tv<;yR*|7^kVpcxblCjn3yzhWY73q^*F&LNXPkDd zn>2}`rNDO-9V+|@v5p5mwGFEIG88>VD(a@fNg2&#*`l?Epe--TI#Y$XSx)c5pAxD* zK>E~@L_rdJ)HaG%E8N>3AnxnhlTLh~&uX&6c5*t^TQDKGWMjs1DPKWa=iM!Iy#O64 zqsf#XF*)s#RiS;!mK{e26y=s+N~i>T8spZ&wTT+i(U++Pp*9i``4Q_K*K#9obby_?`cpjV6^gR{m2xZ*m6P){hCS_qfcw(~%E>uXl^}gZJI*Ym zuH5&jTFDp_(yL2la+xu>B9+FhJQ&y?F+VD6ELb3P^dsw04>~?2AB{K7Boeuz;cW-` zF%C9^^f;(w)2*Zja34k#<&AFO~Wnn_FYEhp?fpXA>Uvz4OT3tjxcXChr z>-}p{Ml!CStu6rsAOqQj33e6Cxa8H~1%zA2`wf}~r~xZ+xqU;fD(ggf8%BLaK=4V) zI5_NRC`hNYlpo#?!h;%r0ner?7q+*M5ha+<_m_|5P<@f(Y+K(*qx-HhD6#^{SKYLO zPC|+Zk}Ul{8m)~<1~;UiLyuZjzLGv}V08n&Gev;1kXuR0u^zf-KT0$Z!)U9M!TK6t zw$4hvD#$wqT}R?7HZjSNIG|;Bf@K8o!lOngNOUXjR<_(AK2ru1!93)A@MqNGfEW?Z zN;Y#y(J3Fjh#H(EE`)$oL~*ADjDS*O%Wh>puuTyH>(eDYT0%Ru>8P#R3&ZLkYXe0OlE!7mkVU5+n(!H z#-Di+-EDYJv8^c}AeD{sJq<=AWwDQSs9R3CP<)Hyk4EN>fn>{pfJ6OF zDi|Gd$Ns z;QQ3k!y2#d9*u{pAbma4Is7qE7;dA<6>u5TIp@-kJ(yvXepDtG(5REnqYg7lx2?3P zPsG)*&>y_Gpjg{T3pHnZd|&UbMQE5o zIXTCrMKz7HsS6UHQ$~Xj&t*D(;#6$^0C3c1DAati5P#YgS@bv_LPSLmsN$oR$5FNg zZ7&_M{4xxRc0svl`ODlNaY!ZvVb_YI{iUp1{{Y9UkG;2N1!8$nA)qU z+QfZDL~Z1ZkYf$$K+zh@)HGqS+!~F%mgt3kl=)Is%Lwpl0`lW){{Sii#F=6+8zPz! zPI>83vakh_@M)Qw`^42D4%sJ}`?sdb#ZT`LH{nh$RdP`A+*D%jGB8N|MKl%>+TDf* z<@!?sWRVlA`yLHR`>BD+AB`}@8lT;sX^4rl8N;J5J-SoUc!Kc%0Ie?Tf_`QOnp)j~ z5<`>fIG_Y^9J#=%^2Ktk@{#yd;?6fh@ZIe`jZnH?!lbf?_fz$t26?3hBwCP2g(?KS zjk321kEo{+42%1tPQWLZ3uKY_)KWtc=-|_s0hKv#dPvm?;Xb*hv_WDoDrL`>G6!mm zvyyj=eJazIPn1*=d6~kdXiNy!WKI;CKF~-ZzdDgs0v3@#s_yYI`CN)%J8GV3yQ3Ka z@s7OG7?`0b#MA*~MkSQ}DS-a~-V%1UN7jHuM4i;!Hg^&QInr7@*{BiSC19@&y!BfzN7(cCg`pCfvZb9s7j-lc016Yiu?wuJYt0?BQjb7yP zGt2T9ARe?U

kf*Jr4FDY0qP`%{;Fp zQ%C_xBZ_Hujfz}C@sY+INTD1tDi0m1gsd^pC(MVF(xE0o#F4=jVMt^u$EQkW-Req? zokajpx!Owx9X%?BoE0u!9G)AB*1Lfl?)p|k#%*;FLHok4FGfS$-qNkit$-`6(Jf3- zeBG;`@V&E6+p)!Ux^1yjgWj@}v7`{I0cpI!_|?@->InK`qj*_clT*S9sAXK5#mLbu z#AGYfW74HX%teT$2wdfkY0&)Oqk7h0mV^OJrz|>Edx(s42>`3I{^a8n%cum97ha%K zL$-(5L&EW*=EqZ>7XJWc&Oa(-cAhS@bL3w^bfdJ%gZYZzHpJw_K{XM#en|r(t#b_LruccVI+!E}^fZ>F)ya))pPeJBosNo}`3yI_4s<546bfl@&~ zO0?5BP1)=1QTgh5F=yV30a)7kWRRwuw(3X7{{T>GUGcY2R-YPzGnGGDE!+ym#yK(` z-5)*i%|djj70%a|KTI6|06M-95?BUbbu@g;NX~KF6z(#EX}W}eJ+10UJse_~E;V~{ zeCsB_>dEs8xKV>0@sa37K0hpHY?wauAStH1Zb6Q9P=DIs)6(xumms#%eJWXPBcD6f z93I32Ra--`WAfTGXVx|%iy%dWvspQU@DbZ4nud;G6>3>xw{xF9d{)G8$nbGlg|;V^Tb4 z7*rtCWe{h0IrXQY3Hz5BW!hrCB6@+7^NC`2wKwZf&S^{r8e%q{?z1) z<;sE&wNhYL5^C1q<~5LeQYNQtTV>U=#&gNX6=_~{QWyhFY2qq(L7!8Ptr}PbNbIfw zYkPr6V3smhITaC)Pnix-srBhiTSjmA_r7HV*O5h#Sy-4EV+R|D(uBEU3Y?#5aY9|fpo^FpPCAjr9IQp9zbd5+0E0|1*<6BJ9j2m=NF>jbC_hH0n5I?T$@J09P{#9lf8F?hHt}1w^i9!ADPrrI;v==k{*8>bOLb4XglLy>Y z`Qx{_87cURuO^s^gu>_Agur6llv9#1O*dl%82G4VvYj)KKDef$up3>+;Y|+2`=E6t zfE8X?V=8vIA4()Dse&pWF*fs`tpX6n$&(z1=}b8wiBsrmWn8Z1kRNJ_RtgHgKT2R% z42nqW>}tKtHgb|Bh#dNgm6Zlclk}*VA^X74Qq-dQ1&c9jS(+C0QbjmgYqpF2kEjWN z?I_J!WKe0b%PNzBns*BnWYunBaxb*E_h_3V@~am5)|W8y(&!_9c0q$vG}6Dw(E>fi zSCYokY^>JH&r&GRNi2{A!eX(;*`JOqM@ZPncAxd!$|*MRcJ0Il=vCvkQ(aySXRv zp}u7*0LQ4O3n?5Ok^(zopk%p)j^J@fYjLk?G4kf3Xk=XM6)MlPkQ1+5(q<+kZX%il zyA>E5o$g1lsG1>?OI7(6Bf~H?16#!8><8)%HJ}h@<=O=_?jLorQ$3pnCw*05(c~}$ zQ$n#XowQ_}{uJLZFgR=vrAD$lWkBQJnnQA@^rj)gbp)s%jX4XIKfdeOii38+w2DS~ zHXINE?@!PN$ucM_Bmuq0H3M5fz?kINPi~Zo6avBWX#}qM4TFQ;g>W0?UPcd>AR>Wx zBwS4y81G1AV1Df$V;)HSX@HA)1G)Y96z$67g?!W!!EG-uf_*@!ga$MFnOJ)sX`}_F z#>0_S+&RcqQTS2|o2Ynb!w*qNFCxhdn9p3&wa^EbDMAa>Y_6(Hx8!L=cZ}s z$Uz4LP>}Q>LLVcZJJP9=GnF+iBF0ruUMb+VK2RyGL@-Wv;$nWdp`y2ua~I5LWo9HW z81=<8TfaLSuX-*7G9xBNaZF`9bGV*6RPx6p11xt_e9Lym^!%xTTyKx=({kmq&6={& zE99(|8ApsA-1<^LMxoeIv>?tgPUPg|k4hCm1hGF_U_xB6AhvPVl|hptGx!5TE0!Tw zB9y5?#|D7duX&`{U#X7z!5`eOHG!!7G|;X~HlHk(&U*vJbjvG&mgbR!auc*QsGB+;GVUOBzG20V!gALCI<(4aDN*0hK^(fk@tI44+X8tl)bc8 zஎ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 z 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 literal 0 HcmV?d00001 diff --git a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt index 839d7f22cf..cf87f9cd66 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/MediaDataExtractorTest.kt @@ -4,7 +4,7 @@ import fr.free.nrw.commons.mwapi.MediaResult import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient import io.reactivex.Single -import junit.framework.Assert.assertTrue +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers diff --git a/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt index f75c345684..df1adf81af 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/MediaTest.kt @@ -7,7 +7,7 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class MediaTest { @Test fun displayTitleShouldStripExtension() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt index 1123a55ec6..12c06ee9e9 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/NearbyControllerTest.kt @@ -2,15 +2,15 @@ package fr.free.nrw.commons import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.nearby.NearbyController.loadAttractionsFromLocationToBaseMarkerOptions +import androidx.test.core.app.ApplicationProvider import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class NearbyControllerTest { @Test @@ -18,7 +18,7 @@ class NearbyControllerTest { val location = LatLng(0.0, 0.0, 0f) val options = loadAttractionsFromLocationToBaseMarkerOptions( - location, null, RuntimeEnvironment.application, null) + location, null, ApplicationProvider.getApplicationContext(), null) assertEquals(0, options.size.toLong()) } @@ -28,7 +28,7 @@ class NearbyControllerTest { val location = LatLng(0.0, 0.0, 0f) val options = loadAttractionsFromLocationToBaseMarkerOptions( - location, emptyList(), RuntimeEnvironment.application, emptyList()) + location, emptyList(), ApplicationProvider.getApplicationContext(), emptyList()) assertEquals(0, options.size.toLong()) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt index a1c984fda0..4d168c1d3d 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt @@ -24,7 +24,7 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class BookMarkLocationDaoTest { private val columns = arrayOf(COLUMN_NAME, COLUMN_DESCRIPTION, diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt index 3710c0c5c3..b38722f88c 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/pictures/BookmarkPictureDaoTest.kt @@ -21,7 +21,7 @@ import org.junit.Before import org.junit.Test @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class BookmarkPictureDaoTest { private val columns = arrayOf(COLUMN_MEDIA_NAME, COLUMN_CREATOR) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt index b3870c60f7..9ee2e75deb 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoryDaoTest.kt @@ -21,7 +21,7 @@ import org.robolectric.annotation.Config import java.util.* @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class CategoryDaoTest { private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED, COLUMN_TIMES_USED) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt index 31e66197d9..adbb50ff86 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionDaoTest.kt @@ -23,7 +23,7 @@ import org.robolectric.annotation.Config import java.util.* @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class ContributionDaoTest { private val localUri = "http://example.com/" private val client: ContentProviderClient = mock() diff --git a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt index 0aad6ae448..46d7dfbfc9 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/delete/DeleteHelperTest.kt @@ -7,7 +7,7 @@ import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.notification.NotificationHelper import fr.free.nrw.commons.utils.ViewUtilWrapper -import junit.framework.Assert.* +import org.junit.Assert.* import org.junit.Before import org.junit.Test import org.mockito.InjectMocks diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt index d764c8da7c..a6dbfc50ee 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/recentsearches/RecentSearchesDaoTest.kt @@ -21,7 +21,7 @@ import org.robolectric.annotation.Config import java.util.* @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class RecentSearchesDaoTest { private val columns = arrayOf(COLUMN_ID, COLUMN_NAME, COLUMN_LAST_USED) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.kt index bf0ddf7728..3187d48ff7 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.kt @@ -20,7 +20,7 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [21], application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class ModifierSequenceDaoTest { private val mediaUrl = "http://example.com/" diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt index 04f20f7b96..907b07c746 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt @@ -1,8 +1,8 @@ package fr.free.nrw.commons.mwapi import android.os.Build +import androidx.test.core.app.ApplicationProvider import com.google.gson.Gson -import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.utils.ConfigUtils @@ -17,14 +17,13 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.robolectric.RobolectricTestRunner -import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config import org.wikipedia.util.DateUtil import java.net.URLDecoder import java.util.* @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = intArrayOf(21), application = TestCommonsApplication::class) +@Config(sdk = [21], application = TestCommonsApplication::class) class ApacheHttpClientMediaWikiApiTest { private lateinit var testObject: ApacheHttpClientMediaWikiApi @@ -39,7 +38,7 @@ class ApacheHttpClientMediaWikiApiTest { wikidataServer = MockWebServer() okHttpClient = OkHttpClient() sharedPreferences = mock(JsonKvStore::class.java) - testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", "http://" + wikidataServer.hostName + ":" + wikidataServer.port + "/", sharedPreferences, Gson()) + testObject = ApacheHttpClientMediaWikiApi(ApplicationProvider.getApplicationContext(), "http://" + server.hostName + ":" + server.port + "/", "http://" + wikidataServer.hostName + ":" + wikidataServer.port + "/", sharedPreferences, Gson()) } @After @@ -319,7 +318,7 @@ class ApacheHttpClientMediaWikiApiTest { private fun assertBasicRequestParameters(server: MockWebServer, method: String): RecordedRequest = server.takeRequest().let { assertEquals("/", it.requestUrl.encodedPath()) assertEquals(method, it.method) - assertEquals("Commons/${ConfigUtils.getVersionNameWithSha(RuntimeEnvironment.application)} (https://mediawiki.org/wiki/Apps/Commons) Android/${Build.VERSION.RELEASE}", + assertEquals("Commons/${ConfigUtils.getVersionNameWithSha(ApplicationProvider.getApplicationContext())} (https://mediawiki.org/wiki/Apps/Commons) Android/${Build.VERSION.RELEASE}", it.getHeader("User-Agent")) if ("POST" == method) { assertEquals("application/x-www-form-urlencoded", it.getHeader("Content-Type")) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/OkHttpJsonApiClientTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/OkHttpJsonApiClientTest.kt index 604095f60c..e9766e5ee0 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/OkHttpJsonApiClientTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/OkHttpJsonApiClientTest.kt @@ -29,7 +29,7 @@ import kotlin.random.Random * Mock web server based tests for ok http json api client */ @RunWith(RobolectricTestRunner::class) -@Config(constants = BuildConfig::class, sdk = [23], application = TestCommonsApplication::class) +@Config(sdk = [23], application = TestCommonsApplication::class) class OkHttpJsonApiClientTest { private lateinit var testObject: OkHttpJsonApiClient diff --git a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt index 42e59797ad..d9c808f77f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/review/ReviewHelperTest.kt @@ -3,8 +3,11 @@ package fr.free.nrw.commons.review import fr.free.nrw.commons.Media import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient +import io.reactivex.Observable import io.reactivex.Single -import junit.framework.Assert.assertTrue +import junit.framework.Assert.assertNotNull +import junit.framework.Assert.assertNull +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers @@ -13,6 +16,8 @@ import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.MockitoAnnotations import org.wikipedia.dataclient.mwapi.MwQueryPage +import org.wikipedia.dataclient.mwapi.MwQueryResponse +import org.wikipedia.dataclient.mwapi.MwQueryResult import org.wikipedia.dataclient.mwapi.RecentChange /** @@ -20,6 +25,8 @@ import org.wikipedia.dataclient.mwapi.RecentChange */ class ReviewHelperTest { + @Mock + internal var reviewInterface: ReviewInterface? = null @Mock internal var okHttpJsonApiClient: OkHttpJsonApiClient? = null @Mock @@ -35,6 +42,31 @@ class ReviewHelperTest { @Throws(Exception::class) fun setUp() { 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 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())) .thenReturn(Single.just(false)) - `when`(okHttpJsonApiClient?.getMedia(ArgumentMatchers.anyString(), ArgumentMatchers.anyBoolean())) - .thenReturn(Single.just(mock(Media::class.java))) - val randomMedia = reviewHelper?.randomMedia?.blockingGet() + assertNotNull(randomMedia) assertTrue(randomMedia is Media) + verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString()) } /** * Test scenario when all media is already nominated for deletion */ - @Test(expected = Exception::class) + @Test(expected = RuntimeException::class) 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())) .thenReturn(Single.just(true)) - reviewHelper?.randomMedia?.blockingGet() + val media = reviewHelper?.randomMedia?.blockingGet() + assertNull(media) + verify(reviewInterface, times(1))!!.getRecentChanges(ArgumentMatchers.anyString()) + } + + /** + * 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()) } - fun getMockRecentChange(type: String, title: String, oldRevisionId: Long): RecentChange { + private fun getMockRecentChange(type: String, title: String, oldRevisionId: Long): RecentChange { val recentChange = mock(RecentChange::class.java) `when`(recentChange!!.type).thenReturn(type) `when`(recentChange.title).thenReturn(title) @@ -88,9 +128,7 @@ class ReviewHelperTest { */ @Test fun getFirstRevisionOfFile() { - `when`(okHttpJsonApiClient?.getFirstRevisionOfFile(ArgumentMatchers.anyString())) - .thenReturn(Single.just(mock(MwQueryPage.Revision::class.java))) - val firstRevisionOfFile = reviewHelper?.getFirstRevisionOfFile("Test.jpg")?.blockingGet() + val firstRevisionOfFile = reviewHelper?.getFirstRevisionOfFile("Test.jpg")?.blockingFirst() assertTrue(firstRevisionOfFile is MwQueryPage.Revision) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt new file mode 100644 index 0000000000..6dde026a93 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileMetadataUtilsTest.kt @@ -0,0 +1,23 @@ +package fr.free.nrw.commons.upload + +import androidx.exifinterface.media.ExifInterface.* +import junit.framework.Assert.assertTrue +import org.junit.Test +import java.util.* + +/** + * Test cases for FileMetadataUtils + */ +class FileMetadataUtilsTest { + + /** + * Test method to verify EXIF tags + */ + @Test + fun getTagsFromPref() { + val author = FileMetadataUtils.getTagsFromPref("Author") + val authorRef = arrayOf(TAG_ARTIST, TAG_CAMARA_OWNER_NAME); + + assertTrue(Arrays.deepEquals(author, authorRef)) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/FileProcessorTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileProcessorTest.kt index 3d5803a1c9..98f6a2e226 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/FileProcessorTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/FileProcessorTest.kt @@ -1,6 +1,7 @@ package fr.free.nrw.commons.upload import android.content.SharedPreferences +import androidx.exifinterface.media.ExifInterface import fr.free.nrw.commons.caching.CacheController import fr.free.nrw.commons.mwapi.CategoryApi import org.junit.Before @@ -11,6 +12,9 @@ import org.mockito.MockitoAnnotations import javax.inject.Inject import javax.inject.Named +import java.io.FileInputStream +import java.io.FileOutputStream + class FileProcessorTest { @Mock @@ -35,4 +39,51 @@ class FileProcessorTest { fun processFileCoordinates() { } + + /** + * Test method to verify redaction Exif metadata + */ + @Test + fun redactExifTags() { + /* + val filePathRef: String? = "src/test/data/exif_redact_sample.jpg" + val filePathTmp: String? = "" + System.getProperty("java.io.tmpdir") + "exif_redact_sample_tmp.jpg" + + val inStream = FileInputStream(filePathRef) + val outStream = FileOutputStream(filePathTmp) + val inChannel = inStream.getChannel() + val outChannel = outStream.getChannel() + inChannel.transferTo(0, inChannel.size(), outChannel) + inStream.close() + outStream.close() + + val redactTags = mutableSetOf("Author", "Copyright", "Location", "Camera Model", + "Lens Model", "Serial Numbers", "Software") + + val exifInterface : ExifInterface? = ExifInterface(filePathTmp.toString()) + + var nonEmptyTag = false + for (redactTag in redactTags) { + for (tag in FileMetadataUtils.getTagsFromPref(redactTag)) { + val tagValue = exifInterface?.getAttribute(tag) + if(tagValue != null) { + nonEmptyTag = true + break + } + } + if (nonEmptyTag) break + } + // all tags are empty, can't test redaction + assert(nonEmptyTag) + + FileProcessor.redactExifTags(exifInterface, redactTags) + + for (redactTag in redactTags) { + for (tag in FileMetadataUtils.getTagsFromPref(redactTag)) { + val oldValue = exifInterface?.getAttribute(tag) + assert(oldValue == null) + } + } + */ + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 4bf0b1e188..fc38b3a14a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,7 @@ #Thu Mar 01 15:28:48 IST 2018 org.gradle.jvmargs=-Xmx1536M android.enableBuildCache=true +android.enableUnitTestBinaryResources=true KOTLIN_VERSION=1.3.21 BUTTERKNIFE_VERSION=10.1.0 From aa86057a6973acd464c8d088fdd1bc3afd3cdd07 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:24:08 +0530 Subject: [PATCH 02/17] BugFix IllegalStateException * setRetainState(true), not required with FragmentStatePagerAdapter * Increase the ViewPager's Offscreen Limit, we want all the fragments to be active --- .../main/java/fr/free/nrw/commons/upload/UploadActivity.java | 4 +++- .../java/fr/free/nrw/commons/upload/UploadBaseFragment.java | 1 - .../main/res/layout/fragment_upload_media_detail_fragment.xml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 6ce97ae441..e3e7d07d7d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -13,6 +13,7 @@ import androidx.cardview.widget.CardView; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -138,7 +139,6 @@ private void initThumbnailsRecyclerView() { } private void initViewPager() { - vpUpload.setOffscreenPageLimit(0); uploadImagesAdapter=new UploadImageAdapter(getSupportFragmentManager()); vpUpload.setAdapter(uploadImagesAdapter); vpUpload.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @@ -431,6 +431,8 @@ public void setFragments(List fragments) { } @Override public int getCount() { + //I understand this is not the best way, I will think of something better than this + vpUpload.setOffscreenPageLimit(fragments.size()); return fragments.size(); } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadBaseFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadBaseFragment.java index e3cecab988..afd1a694b5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadBaseFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadBaseFragment.java @@ -19,7 +19,6 @@ public class UploadBaseFragment extends CommonsDaggerSupportFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setRetainInstance(true); } public void setCallback(Callback callback) { diff --git a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml index 18c2fce6d6..2b06e30b65 100644 --- a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml +++ b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml @@ -111,8 +111,8 @@ style="@style/Widget.AppCompat.Button.Borderless" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/white" android:text="@string/previous_image_title_description" + android:padding="2dp" android:textAlignment="textEnd" android:textColor="@color/button_blue" android:visibility="gone" From b7685d539fe0453689d07a0a91cce9fdfa5c3c43 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:50:16 +0530 Subject: [PATCH 03/17] BugFix, clear selected categoris for previous upload session * Clear Selected Categories * Addded JavaDocs for CategoriesModel --- .../nrw/commons/category/CategoriesModel.java | 104 +++++++++++++++--- .../repository/UploadRemoteDataSource.java | 8 ++ .../commons/repository/UploadRepository.java | 3 +- 3 files changed, 96 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java index ed9f108b39..0970f09da0 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java @@ -19,6 +19,9 @@ import io.reactivex.Observable; import timber.log.Timber; +/** + * The model class for categories in upload + */ public class CategoriesModel{ private static final int SEARCH_CATS_LIMIT = 25; @@ -41,13 +44,22 @@ public CategoriesModel(MediaWikiApi mwApi, this.selectedCategories = new ArrayList<>(); } - //region Misc. utility methods + /** + * Sorts CategoryItem by similarity + * @param filter + * @return + */ public Comparator sortBySimilarity(final String filter) { Comparator stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter); return (firstItem, secondItem) -> stringSimilarityComparator .compare(firstItem.getName(), secondItem.getName()); } + /** + * Returns if the item contains an year + * @param item + * @return + */ public boolean containsYear(String item) { //Check for current and previous year to exclude these categories from removal Calendar now = Calendar.getInstance(); @@ -67,6 +79,10 @@ public boolean containsYear(String item) { || (item.matches(".*0s.*") && !item.matches(".*(200|201)0s.*"))); } + /** + * Updates category count in category dao + * @param item + */ public void updateCategoryCount(CategoryItem item) { Category category = categoryDao.find(item.getName()); @@ -78,23 +94,18 @@ public void updateCategoryCount(CategoryItem item) { category.incTimesUsed(); categoryDao.save(category); } - //endregion - - //region Category Caching - public void cacheAll(HashMap> categories) { - categoriesCache.putAll(categories); - } - - public HashMap> getCategoriesCache() { - return categoriesCache; - } boolean cacheContainsKey(String term) { return categoriesCache.containsKey(term); } //endregion - //region Category searching + /** + * Regional category search + * @param term + * @param imageTitleList + * @return + */ public Observable searchAll(String term, List imageTitleList) { //If user hasn't typed anything in yet, get GPS and recent items if (TextUtils.isEmpty(term)) { @@ -128,10 +139,20 @@ public Observable searchCategories(String term, List image .map(s -> new CategoryItem(s, false)); } + /** + * Returns cached categories + * @param term + * @return + */ private ArrayList getCachedCategories(String term) { return categoriesCache.get(term); } + /** + * Returns default categories + * @param titleList + * @return + */ public Observable defaultCategories(List titleList) { Observable directCat = directCategories(); if (hasDirectCategories()) { @@ -148,10 +169,18 @@ public Observable defaultCategories(List titleList) { } } + /** + * Returns if we have a category in DirectKV Store + * @return + */ private boolean hasDirectCategories() { return !directKvStore.getString("Category", "").equals(""); } + /** + * Returns categories in DirectKVStore + * @return + */ private Observable directCategories() { String directCategory = directKvStore.getString("Category", ""); List categoryList = new ArrayList<>(); @@ -164,28 +193,48 @@ private Observable directCategories() { return Observable.fromIterable(categoryList).map(name -> new CategoryItem(name, false)); } + /** + * Returns GPS categories + * @return + */ Observable gpsCategories() { return Observable.fromIterable(gpsCategoryModel.getCategoryList()) .map(name -> new CategoryItem(name, false)); } + /** + * Returns title based categories + * @param titleList + * @return + */ private Observable titleCategories(List titleList) { return Observable.fromIterable(titleList) .concatMap(this::getTitleCategories); } + /** + * Return category for single title + * @param title + * @return + */ private Observable getTitleCategories(String title) { return mwApi.searchTitles(title, SEARCH_CATS_LIMIT) .map(name -> new CategoryItem(name, false)); } + /** + * Returns recent categories + * @return + */ private Observable recentCategories() { return Observable.fromIterable(categoryDao.recentCategories(SEARCH_CATS_LIMIT)) .map(s -> new CategoryItem(s, false)); } - //endregion - //region Category Selection + /** + * Handles category item selection + * @param item + */ public void onCategoryItemClicked(CategoryItem item) { if (item.isSelected()) { selectCategory(item); @@ -195,22 +244,35 @@ public void onCategoryItemClicked(CategoryItem item) { } } + /** + * Select's category + * @param item + */ public void selectCategory(CategoryItem item) { selectedCategories.add(item); } + /** + * Unselect Category + * @param item + */ public void unselectCategory(CategoryItem item) { selectedCategories.remove(item); } - public int selectedCategoriesCount() { - return selectedCategories.size(); - } + /** + * Get Selected Categories + * @return + */ public List getSelectedCategories() { return selectedCategories; } + /** + * Get Categories String List + * @return + */ public List getCategoryStringList() { List output = new ArrayList<>(); for (CategoryItem item : selectedCategories) { @@ -218,6 +280,12 @@ public List getCategoryStringList() { } return output; } - //endregion + /** + * Cleanup the existing in memory cache's + */ + public void cleanUp() { + this.categoriesCache.clear(); + this.selectedCategories.clear(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java index 1043dcd38e..4949fd0588 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java @@ -74,6 +74,14 @@ public void cleanup() { uploadController.cleanup(); } + /** + * Clean up the selected categories + */ + public void clearSelectedCategories(){ + //This needs further refactoring, this should not be here, right now the structure wont suppoort rhis + categoriesModel.cleanUp(); + } + /** * returnt the list of selected categories * diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java index 8366fdd7ac..c6b7a4b861 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java @@ -63,10 +63,11 @@ public void prepareService() { } /** - * asks the local data source to clean up its resources, prepare for a fresh upload + *Prepare for a fresh upload */ public void cleanup() { localDataSource.cleanUp(); + remoteDataSource.clearSelectedCategories(); } /** From c8985bd01cba937806d51dab0e9e2342a9e90305 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:54:20 +0530 Subject: [PATCH 04/17] Code Formatting in app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java --- .../free/nrw/commons/upload/UploadModel.java | 97 ++++++++----------- 1 file changed, 43 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index 4cbdb674b9..65f12354bf 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -3,19 +3,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.net.Uri; - import androidx.annotation.Nullable; - -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import javax.inject.Inject; -import javax.inject.Named; - import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.SessionManager; @@ -28,12 +16,16 @@ import fr.free.nrw.commons.utils.ImageUtils; import io.reactivex.Observable; import io.reactivex.Single; -import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.functions.Consumer; -import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; - +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import timber.log.Timber; @@ -64,12 +56,12 @@ public class UploadModel { @Inject UploadModel(@Named("licenses") List licenses, - @Named("default_preferences") JsonKvStore store, - @Named("licenses_by_name") Map licensesByName, - Context context, - SessionManager sessionManager, - FileProcessor fileProcessor, - ImageProcessingService imageProcessingService) { + @Named("default_preferences") JsonKvStore store, + @Named("licenses_by_name") Map licensesByName, + Context context, + SessionManager sessionManager, + FileProcessor fileProcessor, + ImageProcessingService imageProcessingService) { this.licenses = licenses; this.store = store; this.license = store.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); @@ -87,7 +79,7 @@ public void cleanUp() { compositeDisposable.clear(); fileProcessor.cleanup(); this.items.clear(); - if(this.selectedCategories!=null) { + if (this.selectedCategories != null) { this.selectedCategories.clear(); } } @@ -101,29 +93,20 @@ public void setSelectedCategories(List selectedCategories) { /** * pre process a list of items - * @param uploadableFiles - * @param place - * @param source - * @param similarImageInterface - * @return */ @SuppressLint("CheckResult") Observable preProcessImages(List uploadableFiles, - Place place, - String source, - SimilarImageInterface similarImageInterface) { + Place place, + String source, + SimilarImageInterface similarImageInterface) { return Observable.fromIterable(uploadableFiles) - .map(uploadableFile -> getUploadItem(uploadableFile, place, source, similarImageInterface)); + .map(uploadableFile -> getUploadItem(uploadableFile, place, source, + similarImageInterface)); } /** * pre process a one item at a time - * @param uploadableFile - * @param place - * @param source - * @param similarImageInterface - * @return */ public Observable preProcessImage(UploadableFile uploadableFile, Place place, @@ -137,11 +120,13 @@ public Single getImageQuality(UploadItem uploadItem, boolean checkTitle } private UploadItem getUploadItem(UploadableFile uploadableFile, - Place place, - String source, - SimilarImageInterface similarImageInterface) { - fileProcessor.initFileDetails(Objects.requireNonNull(uploadableFile.getFilePath()), context.getContentResolver()); - UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile.getFileCreatedDate(context); + Place place, + String source, + SimilarImageInterface similarImageInterface) { + fileProcessor.initFileDetails(Objects.requireNonNull(uploadableFile.getFilePath()), + context.getContentResolver()); + UploadableFile.DateTimeWithSource dateTimeWithSource = uploadableFile + .getFileCreatedDate(context); long fileCreatedDate = -1; String createdTimestampSource = ""; if (dateTimeWithSource != null) { @@ -149,16 +134,18 @@ private UploadItem getUploadItem(UploadableFile uploadableFile, createdTimestampSource = dateTimeWithSource.getSource(); } Timber.d("File created date is %d", fileCreatedDate); - GPSExtractor gpsExtractor = fileProcessor.processFileCoordinates(similarImageInterface,context); - UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(), Uri.parse(uploadableFile.getFilePath()), + GPSExtractor gpsExtractor = fileProcessor + .processFileCoordinates(similarImageInterface, context); + UploadItem uploadItem = new UploadItem(uploadableFile.getContentUri(), + Uri.parse(uploadableFile.getFilePath()), uploadableFile.getMimeType(context), source, gpsExtractor, place, fileCreatedDate, createdTimestampSource); - if(place!=null){ + if (place != null) { uploadItem.title.setTitleText(place.name); uploadItem.descriptions.get(0).setDescriptionText(place.getLongDescription()); uploadItem.descriptions.get(0).setLanguageCode("en"); } - if(!items.contains(uploadItem)) { + if (!items.contains(uploadItem)) { items.add(uploadItem); } return uploadItem; @@ -204,8 +191,8 @@ public Observable buildContributions() { if (item.place != null) { contribution.setWikiDataEntityId(item.place.getWikiDataEntityId()); } - if(null==selectedCategories){//Just a fail safe, this should never be null - selectedCategories=new ArrayList<>(); + if (null == selectedCategories) {//Just a fail safe, this should never be null + selectedCategories = new ArrayList<>(); } contribution.setCategories(selectedCategories); contribution.setTag("mimeType", item.mimeType); @@ -226,11 +213,12 @@ public Observable buildContributions() { public void deletePicture(String filePath) { Iterator iterator = items.iterator(); - while (iterator.hasNext()) + while (iterator.hasNext()) { if (iterator.next().mediaUri.toString().contains(filePath)) { iterator.remove(); break; } + } if (items.isEmpty()) { cleanUp(); } @@ -240,7 +228,7 @@ public List getItems() { return items; } - public void updateUploadItem(int index,UploadItem uploadItem) { + public void updateUploadItem(int index, UploadItem uploadItem) { UploadItem uploadItem1 = items.get(index); uploadItem1.setDescriptions(uploadItem.descriptions); uploadItem1.setTitle(uploadItem.title); @@ -248,6 +236,7 @@ public void updateUploadItem(int index,UploadItem uploadItem) { @SuppressWarnings("WeakerAccess") public static class UploadItem { + private final Uri originalContentUri; private final Uri mediaUri; private final String mimeType; @@ -267,10 +256,10 @@ public static class UploadItem { @SuppressLint("CheckResult") UploadItem(Uri originalContentUri, - Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords, - Place place, - long createdTimestamp, - String createdTimestampSource) { + Uri mediaUri, String mimeType, String source, GPSExtractor gpsCoords, + Place place, + long createdTimestamp, + String createdTimestampSource) { this.originalContentUri = originalContentUri; this.createdTimestampSource = createdTimestampSource; title = new Title(); From bce2b78a8ac75bf11a7a366b73835c369c251a7a Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:55:04 +0530 Subject: [PATCH 05/17] Added class level JavaDoc UploadRemoteDataSource --- .../fr/free/nrw/commons/repository/UploadRemoteDataSource.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java index 4949fd0588..8487840ceb 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java @@ -18,6 +18,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +/** + * This class would act as the data source for remote operations for UploadActivity + */ @Singleton public class UploadRemoteDataSource { From 1eb2bc6207dbb06882a3807029932a0d8b10b8c6 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:56:09 +0530 Subject: [PATCH 06/17] Added class level JavaDoc for UploadRepository --- .../java/fr/free/nrw/commons/repository/UploadRepository.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java index c6b7a4b861..429b1be455 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java @@ -15,6 +15,9 @@ import javax.inject.Inject; import javax.inject.Singleton; +/** + * The repository class for UploadActivity + */ @Singleton public class UploadRepository { From 196c27c24a0822ba0549243eb287d872bcb9b254 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:58:25 +0530 Subject: [PATCH 07/17] Added JavaDocs for ThumbnailsAdapter --- .../free/nrw/commons/upload/ThumbnailsAdapter.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.java b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.java index d8e23538cb..b32f90b0be 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.java @@ -21,6 +21,9 @@ import java.util.ArrayList; import java.util.List; +/** + * The adapter class for image thumbnails to be shown while uploading. + */ class ThumbnailsAdapter extends RecyclerView.Adapter { List uploadableFiles; @@ -31,6 +34,10 @@ public ThumbnailsAdapter(Callback callback) { this.callback = callback; } + /** + * Sets the data, the media files + * @param uploadableFiles + */ public void setUploadableFiles( List uploadableFiles) { this.uploadableFiles=uploadableFiles; @@ -68,6 +75,10 @@ public ViewHolder(@NonNull View itemView) { ButterKnife.bind(this, itemView); } + /** + * Binds a row item to the ViewHolder + * @param position + */ public void bind(int position) { UploadableFile uploadableFile = uploadableFiles.get(position); Uri uri = uploadableFile.getMediaUri(); @@ -91,6 +102,9 @@ public void bind(int position) { } } + /** + * Callback used to get the current selected file position + */ interface Callback { int getCurrentSelectedFilePosition(); From f239425a664dddd7b15e24fe078b7c8c46d8b445 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 22:59:55 +0530 Subject: [PATCH 08/17] Added JavaDocs for MediaLicensePresenter, CategoriesPresenter --- .../nrw/commons/upload/categories/CategoriesPresenter.java | 3 +++ .../free/nrw/commons/upload/license/MediaLicensePresenter.java | 3 +++ 2 files changed, 6 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java index f339b5406a..1824fc39f2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java @@ -23,6 +23,9 @@ import timber.log.Timber; +/** + * The presenter class for UploadCategoriesFragment + */ @Singleton public class CategoriesPresenter implements CategoriesContract.UserActionListener { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.java index b7900ece99..881f21369c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.java @@ -12,6 +12,9 @@ import timber.log.Timber; +/** + * Added JavaDocs for MediaLicensePresenter + */ public class MediaLicensePresenter implements MediaLicenseContract.UserActionListener { private static final MediaLicenseContract.View DUMMY = (MediaLicenseContract.View) Proxy From 2e33ef7cd6b29b60f4ab6edfa64621e68b24c366 Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 23:09:28 +0530 Subject: [PATCH 09/17] Removed null check on category query * Show default catgeories based on image title and gps location when category text empty * Allow search for empty category search --- .../categories/UploadCategoriesFragment.java | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java index ef8b773d2e..2b8b6f848a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java @@ -1,35 +1,23 @@ package fr.free.nrw.commons.upload.categories; import android.os.Bundle; -import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.FrameLayout; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatButton; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputLayout; import com.jakewharton.rxbinding2.view.RxView; import com.jakewharton.rxbinding2.widget.RxTextView; import com.pedrogomez.renderers.RVRendererAdapter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; import fr.free.nrw.commons.R; import fr.free.nrw.commons.category.CategoryClickedListener; import fr.free.nrw.commons.category.CategoryItem; @@ -38,6 +26,10 @@ import fr.free.nrw.commons.utils.DialogUtil; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; import timber.log.Timber; public class UploadCategoriesFragment extends UploadBaseFragment implements CategoriesContract.View, @@ -89,6 +81,8 @@ private void init() { presenter.onAttachView(this); initRecyclerView(); addTextChangeListenerToEtSearch(); + //get default categories for empty query + presenter.searchForCategories(null,mediaTitleList); } private void addTextChangeListenerToEtSearch() { @@ -101,9 +95,7 @@ private void addTextChangeListenerToEtSearch() { } private void searchForCategory(String query) { - if (!TextUtils.isEmpty(query)) { - presenter.searchForCategories(query, mediaTitleList); - } + presenter.searchForCategories(query, mediaTitleList); } private void initRecyclerView() { From 9cde8388f5b5b184b92f7fd4187267ef5cf4f62c Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 2 Jun 2019 21:27:43 +0530 Subject: [PATCH 10/17] Attached image scale listener to upload media image --- .../UploadMediaDetailFragment.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java index 1bdbb743a2..19aa265255 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java @@ -2,6 +2,7 @@ import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -16,6 +17,7 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.github.chrisbanes.photoview.OnScaleChangedListener; import com.github.chrisbanes.photoview.PhotoView; import com.jakewharton.rxbinding2.widget.RxTextView; @@ -155,6 +157,19 @@ private void init() { } else { btnCopyPreviousTitleDesc.setVisibility(View.VISIBLE); } + + attachImageViewScaleChangeListener(); + } + + /** + * Attaches the scale change listener to the image view + */ + private void attachImageViewScaleChangeListener() { + photoViewBackgroundImage.setOnScaleChangeListener( + (scaleFactor, focusX, focusY) -> { + //Whenever the uses plays with the image, lets collapse the media detail container + expandCollapseLlMediaDetail(false); + }); } /** @@ -310,7 +325,15 @@ public void onDestroyView() { @OnClick(R.id.rl_container_title) public void onRlContainerTitleClicked() { - llContainerMediaDetail.setVisibility(isExpanded ? View.GONE : View.VISIBLE); + expandCollapseLlMediaDetail(!isExpanded); + } + + /** + * show hide media detail based on + * @param shouldExpand + */ + private void expandCollapseLlMediaDetail(boolean shouldExpand){ + llContainerMediaDetail.setVisibility(shouldExpand ? View.VISIBLE : View.GONE); isExpanded = !isExpanded; ibExpandCollapse.setRotation(ibExpandCollapse.getRotation() + 180); } From 185b0e48bb639ac41d89e6dbbf7b30fcb319f5c2 Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 29 May 2019 21:05:35 +0530 Subject: [PATCH 11/17] Bug fix, reduced the add description edit text clickable bound --- .../nrw/commons/upload/DescriptionsAdapter.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java b/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java index 03f958dcc2..a24a791bf7 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/DescriptionsAdapter.java @@ -1,7 +1,9 @@ package fr.free.nrw.commons.upload; +import android.content.Context; import android.graphics.drawable.Drawable; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -105,7 +107,8 @@ public void init(int position) { null); descItemEditText.setOnTouchListener((v, event) -> { //2 is for drawable right - if (event.getAction() == MotionEvent.ACTION_UP && (event.getRawX() >= (descItemEditText.getRight() - descItemEditText.getCompoundDrawables()[2].getBounds().width()))) { + float twelveDpInPixels = convertDpToPixel(12, descItemEditText.getContext()); + if (event.getAction() == MotionEvent.ACTION_UP && descItemEditText.getCompoundDrawables()[2].getBounds().contains((int)(descItemEditText.getWidth()-(event.getX()+twelveDpInPixels)),(int)(event.getY()-twelveDpInPixels))){ if (getAdapterPosition() == 0) { callback.showAlert(R.string.media_detail_description, R.string.description_info); @@ -192,4 +195,14 @@ public interface Callback { void showAlert(int mediaDetailDescription, int descriptionInfo); } + + /** + * converts dp to pixel + * @param dp + * @param context + * @return + */ + private float convertDpToPixel(float dp, Context context) { + return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); + } } From 27d768bfa859cecd577ff90aea9fed553fc5595a Mon Sep 17 00:00:00 2001 From: Ashish Date: Sun, 9 Jun 2019 23:48:36 +0530 Subject: [PATCH 12/17] Added tooltip in Title in UploadMediaFragment --- .../UploadMediaDetailFragment.java | 30 +++++++++++++++++++ .../fragment_upload_media_detail_fragment.xml | 1 + 2 files changed, 31 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java index 19aa265255..7d55e82ed1 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.java @@ -1,9 +1,12 @@ package fr.free.nrw.commons.upload.mediaDetails; +import android.content.Context; import android.os.Bundle; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; @@ -159,6 +162,33 @@ private void init() { } attachImageViewScaleChangeListener(); + + addEtTitleTouchListener(); + } + + /** + * Handles the drawable click listener for Edit Text + */ + private void addEtTitleTouchListener() { + etTitle.setOnTouchListener((v, event) -> { + //2 is for drawable right + float twelveDpInPixels = convertDpToPixel(12, getContext()); + if (event.getAction() == MotionEvent.ACTION_UP && etTitle.getCompoundDrawables()[2].getBounds().contains((int)(etTitle.getWidth()-(event.getX()+twelveDpInPixels)),(int)(event.getY()-twelveDpInPixels))){ + showInfoAlert(R.string.media_detail_title,R.string.title_info); + return true; + } + return false; + }); + } + + /** + * converts dp to pixel + * @param dp + * @param context + * @return + */ + private float convertDpToPixel(float dp, Context context) { + return dp * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } /** diff --git a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml index 2b06e30b65..84003afa6e 100644 --- a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml +++ b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml @@ -93,6 +93,7 @@ android:layout_height="wrap_content" android:hint="@string/share_title_hint" android:imeOptions="actionNext" + android:drawableEnd="@drawable/mapbox_info_icon_default" android:inputType="text" android:maxLines="1" /> From 1c2eb074a570fe7d596283201ec39d8f83f7d033 Mon Sep 17 00:00:00 2001 From: Ashish Date: Mon, 10 Jun 2019 01:27:51 +0530 Subject: [PATCH 13/17] BugFix recent categories --- .../nrw/commons/category/CategoriesModel.java | 57 ++++--------------- .../repository/UploadRemoteDataSource.java | 21 ------- .../commons/repository/UploadRepository.java | 21 ------- .../nrw/commons/upload/UploadActivity.java | 1 - .../nrw/commons/upload/UploadContract.java | 2 - .../nrw/commons/upload/UploadPresenter.java | 10 ---- .../upload/categories/CategoriesContract.java | 3 +- .../categories/CategoriesPresenter.java | 38 ++++++++----- .../categories/UploadCategoriesFragment.java | 29 ++++++++-- .../commons/upload/CategoriesPresenterTest.kt | 2 +- 10 files changed, 62 insertions(+), 122 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java index 0970f09da0..9b084da491 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java @@ -1,22 +1,19 @@ package fr.free.nrw.commons.category; import android.text.TextUtils; - +import fr.free.nrw.commons.kvstore.JsonKvStore; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.upload.GpsCategoryModel; +import fr.free.nrw.commons.utils.StringSortingUtils; +import io.reactivex.Observable; import java.util.ArrayList; import java.util.Calendar; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.List; - import javax.inject.Inject; import javax.inject.Named; - -import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.upload.GpsCategoryModel; -import fr.free.nrw.commons.utils.StringSortingUtils; -import io.reactivex.Observable; import timber.log.Timber; /** @@ -107,11 +104,14 @@ boolean cacheContainsKey(String term) { * @return */ public Observable searchAll(String term, List imageTitleList) { - //If user hasn't typed anything in yet, get GPS and recent items + //If query text is empty, show him category based on gps and title and recent searches if (TextUtils.isEmpty(term)) { - return gpsCategories() - .concatWith(titleCategories(imageTitleList)) - .concatWith(recentCategories()); + Observable categoryItemObservable = gpsCategories() + .concatWith(titleCategories(imageTitleList)); + if (hasDirectCategories()) { + categoryItemObservable.concatWith(directCategories().concatWith(recentCategories())); + } + return categoryItemObservable; } //if user types in something that is in cache, return cached category @@ -126,18 +126,6 @@ public Observable searchAll(String term, List imageTitleLi .map(name -> new CategoryItem(name, false)); } - public Observable searchCategories(String term, List imageTitleList) { - //If user hasn't typed anything in yet, get GPS and recent items - if (TextUtils.isEmpty(term)) { - return gpsCategories() - .concatWith(titleCategories(imageTitleList)) - .concatWith(recentCategories()); - } - - return mwApi - .searchCategories(term, SEARCH_CATS_LIMIT) - .map(s -> new CategoryItem(s, false)); - } /** * Returns cached categories @@ -148,27 +136,6 @@ private ArrayList getCachedCategories(String term) { return categoriesCache.get(term); } - /** - * Returns default categories - * @param titleList - * @return - */ - public Observable defaultCategories(List titleList) { - Observable directCat = directCategories(); - if (hasDirectCategories()) { - Timber.d("Image has direct Cat"); - return directCat - .concatWith(gpsCategories()) - .concatWith(titleCategories(titleList)) - .concatWith(recentCategories()); - } else { - Timber.d("Image has no direct Cat"); - return gpsCategories() - .concatWith(titleCategories(titleList)) - .concatWith(recentCategories()); - } - } - /** * Returns if we have a category in DirectKV Store * @return diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java index 8487840ceb..938b6f30d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRemoteDataSource.java @@ -105,27 +105,6 @@ public Observable searchAll(String query, List imageTitleL return categoriesModel.searchAll(query, imageTitleList); } - /** - * searchCategories from MWApi - * - * @param query - * @param imageTitleList - * @return - */ - public Observable searchCategories(String query, List imageTitleList) { - return categoriesModel.searchCategories(query, imageTitleList); - } - - /** - * returns the list of default categoies - * - * @param imageTitleList - * @return - */ - public Observable defaultCategories(List imageTitleList) { - return categoriesModel.defaultCategories(imageTitleList); - } - /** * returns the string list of categories * diff --git a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java index 429b1be455..dbd0f6134d 100644 --- a/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java +++ b/app/src/main/java/fr/free/nrw/commons/repository/UploadRepository.java @@ -93,27 +93,6 @@ public Observable searchAll(String query, List imageTitleL return remoteDataSource.searchAll(query, imageTitleList); } - /** - * searchCategories from MWApi - * - * @param query - * @param imageTitleList - * @return - */ - public Observable searchCategories(String query, List imageTitleList) { - return remoteDataSource.searchCategories(query, imageTitleList); - } - - /** - * returns the default categorories for the list of images based on their titles - * - * @param imageTitleList - * @return - */ - public Observable defaultCategories(List imageTitleList) { - return remoteDataSource.defaultCategories(imageTitleList); - } - /** * returns the string list of categories * diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index e3e7d07d7d..76c409838f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -331,7 +331,6 @@ public int getTotalNumberOfSteps() { } uploadCategoriesFragment = new UploadCategoriesFragment(); - uploadCategoriesFragment.setMediaTitleList(presenter.getImageTitleList()); uploadCategoriesFragment.setCallback(this); mediaLicenseFragment = new MediaLicenseFragment(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java index 6df8f5388f..f90496da01 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadContract.java @@ -35,8 +35,6 @@ public interface UserActionListener extends BasePresenter { void handleSubmit(); - List getImageTitleList(); - void deletePictureAtIndex(int index); } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java index 85e2bd8aa5..a08a547a9b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java @@ -86,16 +86,6 @@ public void onComplete() { } } - public List getImageTitleList() { - List titleList = new ArrayList<>(); - for (UploadItem item : repository.getUploads()) { - if (item.getTitle().isSet()) { - titleList.add(item.getTitle().toString()); - } - } - return titleList; - } - @Override public void deletePictureAtIndex(int index) { List uploadableFiles = view.getUploadableFiles(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java index c1558a9d0d..6ff51632a8 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesContract.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.upload.categories; import fr.free.nrw.commons.BasePresenter; -import fr.free.nrw.commons.category.CategoryClickedListener; import fr.free.nrw.commons.category.CategoryItem; import java.util.List; @@ -32,7 +31,7 @@ public interface View { public interface UserActionListener extends BasePresenter { - void searchForCategories(String query, List imageTitleList); + void searchForCategories(String query); void verifyCategories(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java index 1824fc39f2..a0a7762462 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/CategoriesPresenter.java @@ -4,23 +4,20 @@ import static fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD; import android.text.TextUtils; - import fr.free.nrw.commons.R; import fr.free.nrw.commons.category.CategoryItem; import fr.free.nrw.commons.repository.UploadRepository; +import fr.free.nrw.commons.upload.UploadModel.UploadItem; import io.reactivex.Observable; import io.reactivex.Scheduler; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; - import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; - import javax.inject.Inject; import javax.inject.Named; import javax.inject.Singleton; - import timber.log.Timber; /** @@ -64,14 +61,14 @@ public void onDetachView() { /** * asks the repository to fetch categories for the query + * @param query * - * @param query - * @param imageTitleList */ @Override - public void searchForCategories(String query, List imageTitleList) { + public void searchForCategories(String query) { List categoryItems = new ArrayList<>(); - Disposable searchCategoriesDisposable = Observable + List imageTitleList = getImageTitleList(); + Observable distinctCategoriesObservable = Observable .fromIterable(repository.getSelectedCategories()) .subscribeOn(ioScheduler) .observeOn(mainThreadScheduler) @@ -83,14 +80,13 @@ public void searchForCategories(String query, List imageTitleList) { .observeOn(ioScheduler) .concatWith( repository.searchAll(query, imageTitleList) - .mergeWith(repository.searchCategories(query, imageTitleList)) - .concatWith(TextUtils.isEmpty(query) - ? repository.defaultCategories(imageTitleList) - : Observable.empty()) ) .filter(categoryItem -> !repository.containsYear(categoryItem.getName())) - .distinct() - .sorted(repository.sortBySimilarity(query)) + .distinct(); + if(!TextUtils.isEmpty(query)) { + distinctCategoriesObservable=distinctCategoriesObservable.sorted(repository.sortBySimilarity(query)); + } + Disposable searchCategoriesDisposable = distinctCategoriesObservable .observeOn(mainThreadScheduler) .subscribe( s -> categoryItems.add(s), @@ -108,6 +104,20 @@ public void searchForCategories(String query, List imageTitleList) { compositeDisposable.add(searchCategoriesDisposable); } + /** + * Returns image title list from UploadItem + * @return + */ + private List getImageTitleList() { + List titleList = new ArrayList<>(); + for (UploadItem item : repository.getUploads()) { + if (item.getTitle().isSet()) { + titleList.add(item.getTitle().toString()); + } + } + return titleList; + } + /** * Verifies the number of categories selected, prompts the user if none selected */ diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java index 2b8b6f848a..48485c17ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.java @@ -49,8 +49,10 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate @Inject CategoriesContract.UserActionListener presenter; private RVRendererAdapter adapter; - private List mediaTitleList; + private List mediaTitleList=new ArrayList<>(); private Disposable subscribe; + private List categories; + private boolean isVisible; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -82,7 +84,14 @@ private void init() { initRecyclerView(); addTextChangeListenerToEtSearch(); //get default categories for empty query - presenter.searchForCategories(null,mediaTitleList); + } + + @Override + public void onResume() { + super.onResume(); + if (presenter != null && isVisible && (categories == null || categories.isEmpty())) { + presenter.searchForCategories(null); + } } private void addTextChangeListenerToEtSearch() { @@ -95,7 +104,7 @@ private void addTextChangeListenerToEtSearch() { } private void searchForCategory(String query) { - presenter.searchForCategories(query, mediaTitleList); + presenter.searchForCategories(query); } private void initRecyclerView() { @@ -130,8 +139,8 @@ public void showError(int stringResourceId) { @Override public void setCategories(List categories) { adapter.clear(); - if (categories == null) { - } else { + if (categories != null) { + this.categories = categories; adapter.addAll(categories); adapter.notifyDataSetChanged(); } @@ -178,4 +187,14 @@ public void onPreviousButtonClicked() { public void categoryClicked(CategoryItem item) { presenter.onCategoryItemClicked(item); } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + isVisible = isVisibleToUser; + + if (presenter != null && isResumed() && (categories == null || categories.isEmpty())) { + presenter.searchForCategories(null); + } + } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt index b322fb311f..169f213cd3 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt @@ -60,7 +60,7 @@ class CategoriesPresenterTest { Mockito.`when`(repository?.searchCategories(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty()) Mockito.`when`(repository?.searchAll(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty()) Mockito.`when`(repository?.defaultCategories(ArgumentMatchers.anyList())).thenReturn(Observable.empty()) - categoriesPresenter?.searchForCategories("test", imageTitleList) + categoriesPresenter?.searchForCategories("test") verify(view)?.showProgress(true) verify(view)?.showError(null) verify(view)?.setCategories(null) From 7d717ea3fe2f8c9fc79a274d37c13fec68cfe95f Mon Sep 17 00:00:00 2001 From: Ashish Date: Mon, 10 Jun 2019 01:29:29 +0530 Subject: [PATCH 14/17] Updated test methods --- .../fr/free/nrw/commons/upload/CategoriesPresenterTest.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt index 169f213cd3..eae8defc58 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/CategoriesPresenterTest.kt @@ -57,9 +57,7 @@ class CategoriesPresenterTest { fun searchForCategoriesTest() { Mockito.`when`(repository?.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator { _, _ -> 1 }) Mockito.`when`(repository?.selectedCategories).thenReturn(categoryItems) - Mockito.`when`(repository?.searchCategories(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty()) Mockito.`when`(repository?.searchAll(ArgumentMatchers.anyString(), ArgumentMatchers.anyList())).thenReturn(Observable.empty()) - Mockito.`when`(repository?.defaultCategories(ArgumentMatchers.anyList())).thenReturn(Observable.empty()) categoriesPresenter?.searchForCategories("test") verify(view)?.showProgress(true) verify(view)?.showError(null) From 59e7c3a9b86d8356e89eae365b1600c84c752976 Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 12 Jun 2019 02:10:27 +0530 Subject: [PATCH 15/17] Avoid memory leak, free the adpater in MediaLicenseFragment.onDestroyView --- .../free/nrw/commons/upload/license/MediaLicenseFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.java index 98e11d9e79..8836c9bdfc 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.java @@ -18,7 +18,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatButton; import java.util.List; @@ -164,6 +163,8 @@ private void launchBrowser(String hyperLink) { public void onDestroyView() { super.onDestroyView(); presenter.onDetachView(); + //Free the adapter to avoid memory leaks + adapter=null; } From 910cf86719ad6b7dafa183d5ccfa089c9ac4844d Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 12 Jun 2019 15:57:36 +0530 Subject: [PATCH 16/17] BugFix Illegal State Exception in ViewpPagerAdapter --- .../main/java/fr/free/nrw/commons/upload/UploadActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 055f63c7dd..662986795c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -341,6 +341,7 @@ public int getTotalNumberOfSteps() { fragments.add(mediaLicenseFragment); uploadImagesAdapter.setFragments(fragments); + vpUpload.setOffscreenPageLimit(fragments.size()); } } @@ -431,7 +432,6 @@ public void setFragments(List fragments) { @Override public int getCount() { //I understand this is not the best way, I will think of something better than this - vpUpload.setOffscreenPageLimit(fragments.size()); return fragments.size(); } From 44b177e2abb10160ec9e6296da602430cacad278 Mon Sep 17 00:00:00 2001 From: Ashish Date: Wed, 12 Jun 2019 16:01:49 +0530 Subject: [PATCH 17/17] Remove irrelevant comment --- app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 662986795c..8881151e18 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -431,7 +431,6 @@ public void setFragments(List fragments) { } @Override public int getCount() { - //I understand this is not the best way, I will think of something better than this return fragments.size(); }