From f8cb469b48c2f33d1c8cbcb5542dcf5424ac66ae Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:33:21 +1000 Subject: [PATCH 01/94] Create new UploadUtils class --- app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java new file mode 100644 index 000000000..f6b82ba1d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java @@ -0,0 +1,4 @@ +package fr.free.nrw.commons.upload; + +public class UploadUtils { +} From 7f58b21fa0419fe48685b499d8ceec3c0253cde0 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:52:49 +1000 Subject: [PATCH 02/94] Add Javadocs to ShareActivity.java --- .../free/nrw/commons/upload/ShareActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 6a59c8e30..69e68c245 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -148,9 +148,9 @@ public class ShareActivity private FloatingActionButton mainFab; private boolean isFABOpen = false; - /** * Called when user taps the submit button. + * Requests Storage permission, if needed. */ @Override public void uploadActionInitiated(String title, String description) { @@ -159,8 +159,6 @@ public class ShareActivity this.description = description; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - // Check for Storage permission that is required for upload. - // Do not allow user to proceed without permission, otherwise will crash if (needsToRequestStoragePermission()) { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_PERM_ON_SUBMIT_STORAGE); @@ -172,16 +170,22 @@ public class ShareActivity } } + /** + * Checks whether storage permissions need to be requested. + * Permissions are needed if the file is not owned by this application, (e.g. shared from the Gallery) + * @return true if file is not owned by this application and permission hasn't been granted beforehand + */ @RequiresApi(16) private boolean needsToRequestStoragePermission() { - // We need to ask storage permission when - // the file is not owned by this application, (e.g. shared from the Gallery) - // and permission is not obtained. return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri) && (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED); } + /** + * Called after permission checks are done. + * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController + */ private void uploadBegins() { getFileMetadata(locationPermitted); From b1f777c674fdb773d310761464cc75c5688f55d5 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 8 May 2018 19:54:13 +1000 Subject: [PATCH 03/94] Add TODOs --- app/src/main/AndroidManifest.xml | 1 + .../java/fr/free/nrw/commons/upload/MultipleShareActivity.java | 2 ++ 2 files changed, 3 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17f6770d2..e617b385e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -52,6 +52,7 @@ + Date: Tue, 8 May 2018 20:21:10 +1000 Subject: [PATCH 04/94] Add TODOs and Javadocs --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 7 ++++++- .../main/java/fr/free/nrw/commons/upload/UploadUtils.java | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 69e68c245..1a73c6f43 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -204,6 +204,9 @@ public class ShareActivity }); } + /** + * Starts CategorizationFragment after uploadBegins. + */ private void showPostUpload() { if (categorizationFragment == null) { categorizationFragment = new CategorizationFragment(); @@ -787,6 +790,7 @@ public class ShareActivity return super.onOptionsItemSelected(item); } + //TODO: Move this to a new class. /* * Get SHA1 of file from input stream */ @@ -826,6 +830,7 @@ public class ShareActivity } } + //TODO: Move this to a new class. Save references to the findViewByIds and pass them to the new method /* * function to provide pinch zoom */ @@ -874,7 +879,7 @@ public class ShareActivity expandedImageView.setImageBitmap(scaled); - + // Calculate the starting and ending bounds for the zoomed-in image. // This step involves lots of math. Yay, math. final Rect startBounds = new Rect(); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java index f6b82ba1d..0c0703ca6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java @@ -1,4 +1,7 @@ package fr.free.nrw.commons.upload; public class UploadUtils { + + + } From f4cef8da1b83376bbb693a27dc846bb9f6aeb070 Mon Sep 17 00:00:00 2001 From: misaochan Date: Wed, 9 May 2018 02:29:52 +1000 Subject: [PATCH 05/94] Add TODOs --- .../nrw/commons/upload/ShareActivity.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 1a73c6f43..ffc965d90 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -216,6 +216,10 @@ public class ShareActivity .commit(); } + /** + * Send categories to modifications queue after they are selected + * @param categories categories selected + */ @Override public void onCategoriesSave(List categories) { if (categories.size() > 0) { @@ -253,10 +257,6 @@ public class ShareActivity finish(); } - protected boolean isNearbyUpload() { - return isNearbyUpload; - } - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -309,8 +309,6 @@ public class ShareActivity } }); - - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @@ -345,6 +343,7 @@ public class ShareActivity } } + //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY // Check storage permissions if marshmallow or newer if (useNewPermissions && (!storagePermitted || !locationPermitted)) { if (!storagePermitted && !locationPermitted) { @@ -404,7 +403,8 @@ public class ShareActivity } }); } - /* + + /** * Function to display the zoom and map FAB */ private void showFABMenu(){ @@ -419,8 +419,8 @@ public class ShareActivity zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); } - /* - * function to close the zoom and map FAB + /** + * Function to close the zoom and map FAB */ private void closeFABMenu(){ isFABOpen=false; @@ -429,7 +429,6 @@ public class ShareActivity zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { - } @Override @@ -438,21 +437,25 @@ public class ShareActivity maps_fragment.setVisibility(View.GONE); zoomInButton.setVisibility(View.GONE); } - } @Override public void onAnimationCancel(Animator animator) { - } @Override public void onAnimationRepeat(Animator animator) { - } }); } + /** + * Checks if upload was initiated via Nearby + * @return true if upload was initiated via Nearby + */ + protected boolean isNearbyUpload() { + return isNearbyUpload; + } @Override public void onRequestPermissionsResult(int requestCode, From 625f58259889378790e12c9d13dc94908e0b0dee Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:52:21 +1000 Subject: [PATCH 06/94] Remove MultipleShareActivity from manifest and send SEND_MULTIPLE intent to ShareActivity as well --- app/src/main/AndroidManifest.xml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e617b385e..aaba6abc8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -46,18 +46,6 @@ android:label="@string/app_name"> - - - - - - - - - From 66245d25a5c6b1a9b94ec0093da71fe147e716e8 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:56:47 +1000 Subject: [PATCH 07/94] Remove check for Storage permissions and snackbar in onCreate() --- .../nrw/commons/upload/ShareActivity.java | 37 +++---------------- 1 file changed, 5 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index ffc965d90..27cb3d4f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -334,10 +334,6 @@ public class ShareActivity useNewPermissions = false; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { useNewPermissions = true; - - if (!needsToRequestStoragePermission()) { - storagePermitted = true; - } if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { locationPermitted = true; } @@ -345,35 +341,14 @@ public class ShareActivity //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY // Check storage permissions if marshmallow or newer - if (useNewPermissions && (!storagePermitted || !locationPermitted)) { - if (!storagePermitted && !locationPermitted) { - String permissionRationales = - getResources().getString(R.string.read_storage_permission_rationale) + "\n" - + getResources().getString(R.string.location_permission_rationale); - snackbar = requestPermissionUsingSnackBar( - permissionRationales, - new String[]{ - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION); - View snackbarView = snackbar.getView(); - TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); - textView.setMaxLines(3); - } else if (!storagePermitted) { - requestPermissionUsingSnackBar( - getString(R.string.read_storage_permission_rationale), - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, - REQUEST_PERM_ON_CREATE_STORAGE); - } else if (!locationPermitted) { - requestPermissionUsingSnackBar( - getString(R.string.location_permission_rationale), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_LOCATION); - } + if (!locationPermitted) { + requestPermissionUsingSnackBar( + getString(R.string.location_permission_rationale), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_LOCATION); } performPreUploadProcessingOfFile(); - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); if (shareView == null && categorizationFragment == null) { @@ -389,8 +364,6 @@ public class ShareActivity if( imageObj == null || imageObj.imageCoordsExists != true){ maps_fragment.setVisibility(View.INVISIBLE); } - - maps_fragment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { From c0c74f68d2211c6bf8939d9e61115c2f66282bd1 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 19:57:22 +1000 Subject: [PATCH 08/94] Clear lint warnings --- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 27cb3d4f4..b21f510b1 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -361,13 +361,13 @@ public class ShareActivity uploadController.prepareService(); maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); maps_fragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists != true){ + if( imageObj == null || imageObj.imageCoordsExists){ maps_fragment.setVisibility(View.INVISIBLE); } maps_fragment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists == true) { + if( imageObj != null && imageObj.imageCoordsExists) { Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); From aa731659645d6592a9f2ad109824befe1e6295f6 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:03:46 +1000 Subject: [PATCH 09/94] Create new receiveIntent() method to tidy onCreate --- .../nrw/commons/upload/ShareActivity.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index b21f510b1..918fe68c7 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -257,23 +257,10 @@ public class ShareActivity finish(); } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - //Receive intent from ContributionController.java when user selects picture to upload + /** + * Receive intent from ContributionController.java when user selects picture to upload + */ + private void receiveIntent() { Intent intent = getIntent(); if (Intent.ACTION_SEND.equals(intent.getAction())) { @@ -293,6 +280,25 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); mainFab = (FloatingActionButton) findViewById(R.id.main_fab); /* @@ -339,8 +345,7 @@ public class ShareActivity } } - //TODO: We should only use snackbar for location permissions, since storage permissions are MANDATORY - // Check storage permissions if marshmallow or newer + // Check location permissions if M or newer for category suggestions, request via snackbar if not present if (!locationPermitted) { requestPermissionUsingSnackBar( getString(R.string.location_permission_rationale), From 11d3517b70f402bf314add72a03bbc6e4804a271 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:07:34 +1000 Subject: [PATCH 10/94] Create initViewsAndListeners() method to tidy onCreate --- .../nrw/commons/upload/ShareActivity.java | 46 ++++++++++--------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 918fe68c7..3da0ef8fe 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -282,28 +282,12 @@ public class ShareActivity } } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - receiveIntent(); - + /** + * Initialize views and setup listeners here for FAB to prevent cluttering onCreate + */ + private void initViewsAndListeners() { mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - /* - * called when upper arrow floating button - */ + //called when upper arrow floating button mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -327,6 +311,26 @@ public class ShareActivity Log.i("exception", e.toString()); } zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); + initViewsAndListeners(); if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); From 3bd421424685070fcd061f3e730900476f390a18 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:09:22 +1000 Subject: [PATCH 11/94] More FAB tidying --- .../nrw/commons/upload/ShareActivity.java | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 3da0ef8fe..a9c5c1260 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -291,9 +291,9 @@ public class ShareActivity mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if(!isFABOpen){ + if(!isFABOpen) { showFABMenu(); - }else{ + } else { closeFABMenu(); } } @@ -307,10 +307,27 @@ public class ShareActivity zoomImageFromThumb(backgroundImageView, mediaUri); } }); - } catch (Exception e){ + } catch (Exception e) { Log.i("exception", e.toString()); } zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + maps_fragment.setVisibility(View.VISIBLE); + if( imageObj == null || imageObj.imageCoordsExists){ + maps_fragment.setVisibility(View.INVISIBLE); + } + maps_fragment.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( imageObj != null && imageObj.imageCoordsExists) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } + }); } @Override @@ -368,22 +385,6 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); - maps_fragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists){ - maps_fragment.setVisibility(View.INVISIBLE); - } - maps_fragment.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists) { - Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); - Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); - mapIntent.setPackage("com.google.android.apps.maps"); - startActivity(mapIntent); - } - } - }); } /** From 478c4900dfd5017c8ce6a397de5be55574e49375 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:13:03 +1000 Subject: [PATCH 12/94] Tidy initViewsAndListeners() --- .../fr/free/nrw/commons/upload/ShareActivity.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index a9c5c1260..9849387b8 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -286,8 +286,12 @@ public class ShareActivity * Initialize views and setup listeners here for FAB to prevent cluttering onCreate */ private void initViewsAndListeners() { + //Main FAB splits into Zoom and Map mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - //called when upper arrow floating button + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); + zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -299,7 +303,6 @@ public class ShareActivity } }); - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @Override @@ -308,11 +311,9 @@ public class ShareActivity } }); } catch (Exception e) { - Log.i("exception", e.toString()); + Timber.e(e); } - zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); maps_fragment.setVisibility(View.VISIBLE); if( imageObj == null || imageObj.imageCoordsExists){ maps_fragment.setVisibility(View.INVISIBLE); From c22d4ce07136006f15b545beb4af13e75daa44c0 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:13:59 +1000 Subject: [PATCH 13/94] Shift helper methods to more logical place --- .../nrw/commons/upload/ShareActivity.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 9849387b8..51527b680 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -257,6 +257,63 @@ public class ShareActivity finish(); } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_share); + ButterKnife.bind(this); + initBack(); + backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); + backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder + .newInstance(getResources()) + .setPlaceholderImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_image_black_24dp, getTheme())) + .setFailureImage(VectorDrawableCompat.create(getResources(), + R.drawable.ic_error_outline_black_24dp, getTheme())) + .build()); + + receiveIntent(); + initViewsAndListeners(); + + if (savedInstanceState != null) { + contribution = savedInstanceState.getParcelable("contribution"); + } + + requestAuthToken(); + + Timber.d("Uri: %s", mediaUri.toString()); + Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); + + useNewPermissions = false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + useNewPermissions = true; + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + locationPermitted = true; + } + } + + // Check location permissions if M or newer for category suggestions, request via snackbar if not present + if (!locationPermitted) { + requestPermissionUsingSnackBar( + getString(R.string.location_permission_rationale), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + REQUEST_PERM_ON_CREATE_LOCATION); + } + performPreUploadProcessingOfFile(); + + SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); + categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); + if (shareView == null && categorizationFragment == null) { + shareView = new SingleUploadFragment(); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.single_upload_fragment_container, shareView, "shareView") + .commitAllowingStateLoss(); + } + uploadController.prepareService(); + } + /** * Receive intent from ContributionController.java when user selects picture to upload */ @@ -330,64 +387,7 @@ public class ShareActivity } }); } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_share); - ButterKnife.bind(this); - initBack(); - backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage); - backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder - .newInstance(getResources()) - .setPlaceholderImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_image_black_24dp, getTheme())) - .setFailureImage(VectorDrawableCompat.create(getResources(), - R.drawable.ic_error_outline_black_24dp, getTheme())) - .build()); - - receiveIntent(); - initViewsAndListeners(); - - if (savedInstanceState != null) { - contribution = savedInstanceState.getParcelable("contribution"); - } - - requestAuthToken(); - - Timber.d("Uri: %s", mediaUri.toString()); - Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); - - useNewPermissions = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - useNewPermissions = true; - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - locationPermitted = true; - } - } - - // Check location permissions if M or newer for category suggestions, request via snackbar if not present - if (!locationPermitted) { - requestPermissionUsingSnackBar( - getString(R.string.location_permission_rationale), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_LOCATION); - } - performPreUploadProcessingOfFile(); - - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); - categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); - if (shareView == null && categorizationFragment == null) { - shareView = new SingleUploadFragment(); - getSupportFragmentManager() - .beginTransaction() - .add(R.id.single_upload_fragment_container, shareView, "shareView") - .commitAllowingStateLoss(); - } - uploadController.prepareService(); - } - + /** * Function to display the zoom and map FAB */ From e62022ab55aa2370b22dae728e6155907bff0790 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:25:42 +1000 Subject: [PATCH 14/94] Tidy up onRequestPermissionsResult() --- .../nrw/commons/upload/ShareActivity.java | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 51527b680..be4d65e27 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -387,11 +387,11 @@ public class ShareActivity } }); } - + /** * Function to display the zoom and map FAB */ - private void showFABMenu(){ + private void showFABMenu() { isFABOpen=true; if( imageObj != null && imageObj.imageCoordsExists == true) @@ -445,42 +445,18 @@ public class ShareActivity public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { - case REQUEST_PERM_ON_CREATE_STORAGE: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - backgroundImageView.setImageURI(mediaUri); - storagePermitted = true; - performPreUploadProcessingOfFile(); - } - return; - } case REQUEST_PERM_ON_CREATE_LOCATION: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - locationPermitted = true; - performPreUploadProcessingOfFile(); - } - return; - } - case REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION: { - if (grantResults.length >= 2 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - backgroundImageView.setImageURI(mediaUri); - storagePermitted = true; - performPreUploadProcessingOfFile(); - } - if (grantResults.length >= 2 - && grantResults[1] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { locationPermitted = true; performPreUploadProcessingOfFile(); } return; } + // Storage (from submit button) - this needs to be separate from (1) because only the // submit button should bring user to next screen case REQUEST_PERM_ON_SUBMIT_STORAGE: { - if (grantResults.length >= 1 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //It is OK to call this at both (1) and (4) because if perm had been granted at //snackbar, user should not be prompted at submit button performPreUploadProcessingOfFile(); @@ -489,7 +465,6 @@ public class ShareActivity uploadBegins(); snackbar.dismiss(); } - return; } } } From b1c3e8a0ac270f7b5dafb2ca49649c30b7626bda Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:41:15 +1000 Subject: [PATCH 15/94] Tidying up code --- .../fr/free/nrw/commons/upload/ShareActivity.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index be4d65e27..567cd27e9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -394,7 +394,7 @@ public class ShareActivity private void showFABMenu() { isFABOpen=true; - if( imageObj != null && imageObj.imageCoordsExists == true) + if( imageObj != null && imageObj.imageCoordsExists) maps_fragment.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); @@ -482,12 +482,10 @@ public class ShareActivity ExistingFileAsync fileAsyncTask = new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { Timber.d("%s duplicate check: %s", mediaUri.toString(), result); - duplicateCheckPassed = (result == DUPLICATE_PROCEED - || result == NO_DUPLICATE); - /* - TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means - we are processing images that are already on server???... - */ + duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); + + //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means + //we are processing images that are already on server???... if (duplicateCheckPassed) { //image can be uploaded, so now check if its a useless picture or not @@ -500,7 +498,6 @@ public class ShareActivity Timber.d(e, "IO Exception: "); } } - getFileMetadata(locationPermitted); } else { Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", From ea5f3a6ea9e2d09b33b81cab1a492fc3a1f46611 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 15 May 2018 20:43:21 +1000 Subject: [PATCH 16/94] Add Javadocs --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 567cd27e9..198792314 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -441,9 +441,14 @@ public class ShareActivity return isNearbyUpload; } + /** + * Handles BOTH snackbar permission request (for location) and submit button permission request (for storage) + * @param requestCode type of request + * @param permissions permissions requested + * @param grantResults grant results + */ @Override - public void onRequestPermissionsResult(int requestCode, - @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_PERM_ON_CREATE_LOCATION: { if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { From c4f55d2fe8a2f0e658a4b443c7aa2fbb70a94ec9 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 7 May 2018 07:54:16 +0200 Subject: [PATCH 17/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-hu/strings.xml | 8 ++++++++ app/src/main/res/values-iw/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 1 + app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 2 ++ app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 13 files changed, 21 insertions(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f265e7ebd..36e013976 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -269,4 +269,5 @@ Fortfahren Abbrechen Erneut versuchen + App teilen diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 58aa93860..0dae5c65e 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -273,4 +273,5 @@ Συνέχεια Ακύρωση Ξαναπροσπαθήστε + Κοινοποίηση εφαρμογής diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 8494a7a36..442fc6bc5 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -279,4 +279,5 @@ Continuer Annuler Réessayer + Partager les applications diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 5524afb0e..81e655b95 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -13,6 +13,7 @@ * ViDam --> + Megjelenés Általános Visszajelzés Helyszín @@ -145,6 +146,7 @@ Híres emberek (a polgármestered, olimpikonok, akikkel találkoztál) Kérjük, NE tölts fel: - Szelfiket vagy képeket a barátaidról\n- Internetröl letöltött képeket\n- Kereskedelmi alkalmazások képernyőképeit + Az Internetről letöltött képek Példa feltöltés: - Cím: Sydney-i Operaház\n- Leírás: A Sydney-i Operaház az öböl túlpartjáról\n- Kategóriák: Sydney Opera House from the west, Sydney Opera House remote views Cím: Sydney-i Operaház @@ -191,6 +193,7 @@ Commons Logo Commons weboldal Commons Facebook-oldal + Commons Github forráskód Háttérkép Nem található kép Kép feltöltése @@ -223,6 +226,8 @@ Hiba a képek gyorsítótárazásakor Egy egyedi, leíró cím a fájlnak, ami fájlnévként fog szolgálni. Egyszerű nyelvezetet használhatsz szóközökkel. Ne tedd bele a kiterjesztést. Kérlek a lehető legteljesebb módon írd le a fájlt: hol készült, mit ábrázol, mi a kontextus? Kérlek add meg az objektumokat vagy személyeket a képen, valamint a nehezen kitalálható információkat (például a kép készítésének dátumát, ha az egy tájkép). Amennyiben a média valami szokatlant ábrázol, kérlek fejtsd ki, hogy mi teszi szokatlanná. + Ez a fénykép túl sötét, biztos fel akarod tölteni? A Wikimédia Commons csak enciklopédikus értékkel bíró képeket tart meg. + Ez a fénykép homályos, biztos fel akarod tölteni? A Wikimédia Commons csak enciklopédikus értékkel bíró képeket tart meg. Engedély adása Külső tárhely használata Az alkalmazáson belüli kamerával készült képek mentése az eszközre @@ -238,6 +243,7 @@ A hely nem érhető el. Közeli helyek listájának megtekintéséhez engedély szükséges Üdvözlünk a Wikimedia Commonson, %1$s! Örülünk, hogy itt vagy. + %1$s üzenetet hagyott a vitalapodon Köszönjük a szerkesztésedet! WIKIDATA WIKIPÉDIA @@ -249,6 +255,8 @@ Internet elérhető Nincs értesítés Nyelvek + Folytatás Mégse Újra + Alkalmazás megosztása diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 48750daf9..762a3be31 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -273,4 +273,5 @@ המשך ביטול לנסות שוב + שיתוף היישום diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 11faee9c6..d0e1c21ed 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -266,4 +266,5 @@ 진행 취소 다시 시도 + 앱 공유 diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index dd1e6f03e..4e44dad60 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -263,4 +263,5 @@ Продолжи Откажи Пробај пак + Сподели прилог diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 0cd748031..30115f1ad 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -263,4 +263,5 @@ Andé anans Anulé Prové torna + Partagé j\'aplicassion diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 4bc478527..6722ae4bb 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -275,4 +275,5 @@ Avançar Cancelar Tentar novamente + Compartilhar o aplicativo diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 5e4862ecb..b0717f3dc 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -159,6 +159,7 @@ Evite materiais protegidos por direitos de autor que tenham sido encontrados na Internet, bem como imagens de cartazes, capas de livros, etc. Acha que conseguiu? Sim! + Categorias A carregar… Nenhuma selecionada diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index fa5174ced..24ef60b61 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -156,6 +156,7 @@ Undvik upphovsrättsskyddat material som du hittar på Internet, samt bilder av affischer, bokomslag, etc. Tror du att du förstår? Ja! + Kategorier Läser in… Ingen markerad @@ -270,4 +271,5 @@ Fortsätt Avbryt Försök igen + Dela app diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 4966d9c08..a2878cddf 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -271,4 +271,5 @@ 已進行 取消 重試 + 分享應用程式 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 83650b408..d3d049ea2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -269,4 +269,5 @@ 已处理 取消 重试 + 分享应用 From 30d7b5d35c7734730933405ed82bf2744076179a Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Mon, 7 May 2018 13:39:23 +0530 Subject: [PATCH 18/94] Integrate API for displaying featured images (#1456) * Integrate API for displaying featured images * Add pagination and refactor code so that it can be reused for category images * Add license info to the images * Fix author view * Remove unused values * Fix minor issues with featured images * Fix null license url issue * Remove some log lines * Fix back navigation issue * fix tests * fix test inits * Gracefully handling various error situations * Added java docs --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 5 +- .../category/CategoryImageController.java | 29 +++ .../commons/category/CategoryImageUtils.java | 225 +++++++++++++++++ .../category/CategoryImagesActivity.java | 160 ++++++++++++ .../category/CategoryImagesListFragment.java | 227 ++++++++++++++++++ .../nrw/commons/category/GridViewAdapter.java | 88 +++++++ .../nrw/commons/category/QueryContinue.java | 24 ++ .../nrw/commons/di/ActivityBuilderModule.java | 4 +- .../commons/di/CommonsApplicationModule.java | 30 ++- .../nrw/commons/di/FragmentBuilderModule.java | 4 +- .../nrw/commons/featured/FeaturedImage.java | 44 ---- .../featured/FeaturedImagesActivity.java | 114 --------- .../featured/FeaturedImagesListFragment.java | 52 ---- .../commons/featured/MockGridViewAdapter.java | 50 ---- .../commons/media/MediaDetailFragment.java | 31 ++- .../mwapi/ApacheHttpClientMediaWikiApi.java | 98 +++++++- .../free/nrw/commons/mwapi/MediaWikiApi.java | 3 + .../commons/theme/NavigationBaseActivity.java | 7 +- .../free/nrw/commons/utils/ContinueUtils.java | 15 ++ ...mages.xml => activity_category_images.xml} | 11 +- ...mages.xml => fragment_category_images.xml} | 15 +- ..._images.xml => layout_category_images.xml} | 10 +- app/src/main/res/values/strings.xml | 4 + .../nrw/commons/TestCommonsApplication.kt | 5 +- .../mwapi/ApacheHttpClientMediaWikiApiTest.kt | 5 +- 26 files changed, 953 insertions(+), 309 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java rename app/src/main/res/layout/{activity_featured_images.xml => activity_category_images.xml} (69%) rename app/src/main/res/layout/{fragment_featured_images.xml => fragment_category_images.xml} (70%) rename app/src/main/res/layout/{layout_featured_images.xml => layout_category_images.xml} (88%) diff --git a/app/build.gradle b/app/build.gradle index 535f58143..9c6f62fd4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,6 +49,8 @@ dependencies { implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0' implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0' + implementation 'org.jsoup:jsoup:1.11.3' + implementation 'com.facebook.fresco:fresco:1.5.0' implementation 'com.facebook.stetho:stetho:1.5.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6aab09b55..17f6770d2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -92,8 +92,9 @@ android:label="@string/navigation_item_notification" /> + android:name=".category.CategoryImagesActivity" + android:label="@string/title_activity_featured_images" + android:parentActivityName=".contributions.ContributionsActivity" /> diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java new file mode 100644 index 000000000..3495d710c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageController.java @@ -0,0 +1,29 @@ +package fr.free.nrw.commons.category; + +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Singleton; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.mwapi.MediaWikiApi; + +@Singleton +public class CategoryImageController { + + private MediaWikiApi mediaWikiApi; + + @Inject + public CategoryImageController(MediaWikiApi mediaWikiApi) { + this.mediaWikiApi = mediaWikiApi; + } + + /** + * Takes a category name as input and calls the API to get a list of images for that category + * @param categoryName + * @return + */ + public List getCategoryImages(String categoryName) { + return mediaWikiApi.getCategoryImages(categoryName); + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java new file mode 100644 index 000000000..18749847e --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImageUtils.java @@ -0,0 +1,225 @@ +package fr.free.nrw.commons.category; + +import org.jsoup.Jsoup; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.annotation.Nullable; + +import fr.free.nrw.commons.Media; +import timber.log.Timber; + +public class CategoryImageUtils { + + /** + * The method iterates over the child nodes to return a list of Media objects + * @param childNodes + * @return + */ + public static List getMediaList(NodeList childNodes) { + List categoryImages = new ArrayList<>(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + categoryImages.add(getMediaFromPage(node)); + } + + return categoryImages; + } + + /** + * Creates a new Media object from the XML response as received by the API + * @param node + * @return + */ + private static Media getMediaFromPage(Node node) { + Media media = new Media(null, + getImageUrl(node), + getFileName(node), + getDescription(node), + getDataLength(node), + getDateCreated(node), + getDateCreated(node), + getCreator(node) + ); + + media.setLicense(getLicense(node)); + + return media; + } + + /** + * Extracts the filename of the uploaded image + * @param document + * @return + */ + private static String getFileName(Node document) { + Element element = (Element) document; + return element.getAttribute("title"); + } + + /** + * Extracts the image description for that particular upload + * @param document + * @return + */ + private static String getDescription(Node document) { + return getMetaDataValue(document, "ImageDescription"); + } + + /** + * Extracts license information from the image meta data + * @param document + * @return + */ + private static String getLicense(Node document) { + return getMetaDataValue(document, "License"); + } + + /** + * Returns the parsed value of artist from the response + * The artist information is returned as a HTML string from the API. Jsoup library parses the HTML string + * to extract just the text value + * @param document + * @return + */ + private static String getCreator(Node document) { + String artist = getMetaDataValue(document, "Artist"); + if (artist != null) { + return Jsoup.parse(artist).text(); + } + return null; + } + + /** + * Returns the parsed date of creation of the image + * @param document + * @return + */ + private static Date getDateCreated(Node document) { + String dateTime = getMetaDataValue(document, "DateTime"); + if (dateTime != null && !dateTime.equals("")) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + try { + return format.parse(dateTime); + } catch (ParseException e) { + Timber.d("Error occurred while parsing date %s", dateTime); + return new Date(); + } + } + return new Date(); + } + + /** + * @param document + * @return Returns the url attribute from the imageInfo node + */ + private static String getImageUrl(Node document) { + Element element = (Element) getImageInfo(document); + if (element != null) { + return element.getAttribute("url"); + } + return null; + } + + /** + * Takes the node document and gives out the attribute length from the node document + * @param document + * @return + */ + private static long getDataLength(Node document) { + Element element = (Element) document; + if (element != null) { + String length = element.getAttribute("length"); + if (length != null && !length.equals("")) { + return Long.parseLong(length); + } + } + return 0L; + } + + /** + * Generic method to get the value of any meta as returned by the getMetaData function + * @param document node document as returned by API + * @param metaName the name of meta node to be returned + * @return + */ + private static String getMetaDataValue(Node document, String metaName) { + Element metaData = getMetaData(document, metaName); + if (metaData != null) { + return metaData.getAttribute("value"); + } + return null; + } + + /** + * Generic method to return an element taking the node document and metaName as input + * @param document node document as returned by API + * @param metaName the name of meta node to be returned + * @return + */ + @Nullable + private static Element getMetaData(Node document, String metaName) { + Node extraMetaData = getExtraMetaData(document); + if (extraMetaData != null) { + Node node = getNode(extraMetaData, metaName); + if (node != null) { + return (Element) node; + } + } + return null; + } + + /** + * Extracts extmetadata from the response XML + * @param document + * @return + */ + @Nullable + private static Node getExtraMetaData(Node document) { + Node imageInfo = getImageInfo(document); + if (imageInfo != null) { + return getNode(imageInfo, "extmetadata"); + } + return null; + } + + /** + * Extracts the ii node from the imageinfo node + * @param document + * @return + */ + @Nullable + private static Node getImageInfo(Node document) { + Node imageInfo = getNode(document, "imageinfo"); + if (imageInfo != null) { + return getNode(imageInfo, "ii"); + } + return null; + } + + /** + * Takes a parent node as input and returns a child node if present + * @param node parent node + * @param nodeName child node name + * @return + */ + @Nullable + public static Node getNode(Node node, String nodeName) { + NodeList childNodes = node.getChildNodes(); + for (int i = 0; i < childNodes.getLength(); i++) { + Node nodeItem = childNodes.item(i); + Element item = (Element) nodeItem; + if (item.getTagName().equals(nodeName)) { + return nodeItem; + } + } + return null; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java new file mode 100644 index 000000000..1f385b258 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java @@ -0,0 +1,160 @@ +package fr.free.nrw.commons.category; + +import android.content.Context; +import android.content.Intent; +import android.database.DataSetObserver; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.AdapterView; + +import butterknife.ButterKnife; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.auth.AuthenticatedActivity; +import fr.free.nrw.commons.media.MediaDetailPagerFragment; +import timber.log.Timber; + +/** + * This activity displays pictures of a particular category + * Its generic and simply takes the name of category name in its start intent to load all images in + * a particular category. This activity is currently being used to display a list of featured images, + * which is nothing but another category on wikimedia commons. + */ + +public class CategoryImagesActivity + extends AuthenticatedActivity + implements FragmentManager.OnBackStackChangedListener, + MediaDetailPagerFragment.MediaDetailProvider, + AdapterView.OnItemClickListener{ + + + private FragmentManager supportFragmentManager; + private CategoryImagesListFragment categoryImagesListFragment; + private MediaDetailPagerFragment mediaDetails; + + @Override + protected void onAuthCookieAcquired(String authCookie) { + + } + + @Override + protected void onAuthFailure() { + + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_category_images); + ButterKnife.bind(this); + + // Activity can call methods in the fragment by acquiring a + // reference to the Fragment from FragmentManager, using findFragmentById() + supportFragmentManager = getSupportFragmentManager(); + setCategoryImagesFragment(); + supportFragmentManager.addOnBackStackChangedListener(this); + if (savedInstanceState != null) { + mediaDetails = (MediaDetailPagerFragment) supportFragmentManager + .findFragmentById(R.id.fragmentContainer); + + } + requestAuthToken(); + initDrawer(); + setPageTitle(); + } + + /** + * Gets the categoryName from the intent and initializes the fragment for showing images of that category + */ + private void setCategoryImagesFragment() { + categoryImagesListFragment = new CategoryImagesListFragment(); + String categoryName = getIntent().getStringExtra("categoryName"); + if (getIntent() != null && categoryName != null) { + Bundle arguments = new Bundle(); + arguments.putString("categoryName", categoryName); + categoryImagesListFragment.setArguments(arguments); + FragmentTransaction transaction = supportFragmentManager.beginTransaction(); + transaction + .add(R.id.fragmentContainer, categoryImagesListFragment) + .commit(); + } + } + + /** + * Gets the passed title from the intents and displays it as the page title + */ + private void setPageTitle() { + if (getIntent() != null && getIntent().getStringExtra("title") != null) { + setTitle(getIntent().getStringExtra("title")); + } + } + + @Override + public void onBackStackChanged() { + } + + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + if (mediaDetails == null || !mediaDetails.isVisible()) { + // set isFeaturedImage true for featured images, to include author field on media detail + mediaDetails = new MediaDetailPagerFragment(false, true); + FragmentManager supportFragmentManager = getSupportFragmentManager(); + supportFragmentManager + .beginTransaction() + .replace(R.id.fragmentContainer, mediaDetails) + .addToBackStack(null) + .commit(); + supportFragmentManager.executePendingTransactions(); + } + mediaDetails.showImage(i); + } + + /** + * Consumers should be simply using this method to use this activity. + * @param context + * @param title Page title + * @param categoryName Name of the category for displaying its images + */ + public static void startYourself(Context context, String title, String categoryName) { + Intent intent = new Intent(context, CategoryImagesActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.putExtra("title", title); + intent.putExtra("categoryName", categoryName); + context.startActivity(intent); + } + + @Override + public Media getMediaAtPosition(int i) { + if (categoryImagesListFragment.getAdapter() == null) { + // not yet ready to return data + return null; + } else { + return (Media) categoryImagesListFragment.getAdapter().getItem(i); + } + } + + @Override + public int getTotalMediaCount() { + if (categoryImagesListFragment.getAdapter() == null) { + return 0; + } + return categoryImagesListFragment.getAdapter().getCount(); + } + + @Override + public void notifyDatasetChanged() { + + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java new file mode 100644 index 000000000..3b6734edd --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -0,0 +1,227 @@ +package fr.free.nrw.commons.category; + +import android.annotation.SuppressLint; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.GridView; +import android.widget.ListAdapter; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import butterknife.BindView; +import butterknife.ButterKnife; +import dagger.android.support.DaggerFragment; +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.utils.NetworkUtils; +import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +/** + * Displays images for a particular category with load more on scrolling incorporated + */ +public class CategoryImagesListFragment extends DaggerFragment { + + private static int TIMEOUT_SECONDS = 15; + + private GridViewAdapter gridAdapter; + + @BindView(R.id.statusMessage) + TextView statusTextView; + @BindView(R.id.loadingImagesProgressBar) ProgressBar progressBar; + @BindView(R.id.categoryImagesList) GridView gridView; + + private boolean hasMoreImages = true; + private boolean isLoading; + private String categoryName = null; + + @Inject CategoryImageController controller; + @Inject @Named("category_prefs") SharedPreferences categoryPreferences; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_category_images, container, false); + ButterKnife.bind(this, v); + return v; + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + gridView.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); + initViews(); + } + + /** + * Initializes the UI elements for the fragment + * Setup the grid view to and scroll listener for it + */ + private void initViews() { + String categoryName = getArguments().getString("categoryName"); + if (getArguments() != null && categoryName != null) { + this.categoryName = categoryName; + resetQueryContinueValues(categoryName); + initList(); + setScrollListener(); + } + } + + /** + * Query continue values determine the last page that was loaded for the particular keyword + * This method resets those values, so that the results can be queried from the first page itself + * @param keyword + */ + private void resetQueryContinueValues(String keyword) { + SharedPreferences.Editor editor = categoryPreferences.edit(); + editor.remove(keyword); + editor.apply(); + } + + /** + * Checks for internet connection and then initializes the grid view with first 10 images of that category + */ + @SuppressLint("CheckResult") + private void initList() { + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + + isLoading = true; + progressBar.setVisibility(VISIBLE); + Observable.fromCallable(() -> controller.getCategoryImages(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + /** + * Handles the UI updates for no internet scenario + */ + private void handleNoInternet() { + progressBar.setVisibility(GONE); + if (gridAdapter == null || gridAdapter.isEmpty()) { + statusTextView.setVisibility(VISIBLE); + statusTextView.setText(getString(R.string.no_internet)); + } else { + ViewUtil.showSnackbar(gridView, R.string.no_internet); + } + } + + /** + * Logs and handles API error scenario + * @param throwable + */ + private void handleError(Throwable throwable) { + Timber.e(throwable, "Error occurred while loading featured images"); + initErrorView(); + } + + /** + * Handles the UI updates for a error scenario + */ + private void initErrorView() { + ViewUtil.showSnackbar(gridView, R.string.error_loading_images); + progressBar.setVisibility(GONE); + if (gridAdapter == null || gridAdapter.isEmpty()) { + statusTextView.setVisibility(VISIBLE); + statusTextView.setText(getString(R.string.no_images_found)); + } else { + statusTextView.setVisibility(GONE); + } + } + + /** + * Initializes the adapter with a list of Media objects + * @param mediaList + */ + private void setAdapter(List mediaList) { + gridAdapter = new GridViewAdapter(this.getContext(), R.layout.layout_category_images, mediaList); + gridView.setAdapter(gridAdapter); + } + + /** + * Sets the scroll listener for the grid view so that more images are fetched when the user scrolls down + * Checks if the category has more images before loading + * Also checks whether images are currently being fetched before triggering another request + */ + private void setScrollListener() { + gridView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + if (hasMoreImages && !isLoading && (firstVisibleItem + visibleItemCount + 1 >= totalItemCount)) { + isLoading = true; + fetchMoreImages(); + } + } + }); + } + + /** + * Fetches more images for the category and adds it to the grid view adapter + */ + @SuppressLint("CheckResult") + private void fetchMoreImages() { + if(!NetworkUtils.isInternetConnectionEstablished(getContext())) { + handleNoInternet(); + return; + } + + progressBar.setVisibility(VISIBLE); + Observable.fromCallable(() -> controller.getCategoryImages(categoryName)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .timeout(TIMEOUT_SECONDS, TimeUnit.SECONDS) + .subscribe(this::handleSuccess, this::handleError); + } + + /** + * Handles the success scenario + * On first load, it initializes the grid view. On subsequent loads, it adds items to the adapter + * @param collection + */ + private void handleSuccess(List collection) { + if(collection == null || collection.isEmpty()) { + initErrorView(); + hasMoreImages = false; + return; + } + + if(gridAdapter == null) { + setAdapter(collection); + } else { + gridAdapter.addItems(collection); + } + + progressBar.setVisibility(GONE); + isLoading = false; + statusTextView.setVisibility(GONE); + } + + public ListAdapter getAdapter() { + return gridView.getAdapter(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java new file mode 100644 index 000000000..c8e6066f6 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/GridViewAdapter.java @@ -0,0 +1,88 @@ +package fr.free.nrw.commons.category; + +import android.app.Activity; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +import fr.free.nrw.commons.Media; +import fr.free.nrw.commons.MediaWikiImageView; +import fr.free.nrw.commons.R; + +/** + * This is created to only display UI implementation. Needs to be changed in real implementation + */ + +public class GridViewAdapter extends ArrayAdapter { + private Context context; + private List data; + + public GridViewAdapter(Context context, int layoutResourceId, List data) { + super(context, layoutResourceId, data); + this.context = context; + this.data = data; + } + + /** + * Adds more item to the list + * Its triggered on scrolling down in the list + * @param images + */ + public void addItems(List images) { + if (data == null) { + data = new ArrayList<>(); + } + data.addAll(images); + notifyDataSetChanged(); + } + + @Override + public boolean isEmpty() { + return data == null || data.isEmpty(); + } + + /** + * Sets up the UI for the category image item + * @param position + * @param convertView + * @param parent + * @return + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + if (convertView == null) { + LayoutInflater inflater = ((Activity) context).getLayoutInflater(); + convertView = inflater.inflate(R.layout.layout_category_images, null); + } + + Media item = data.get(position); + MediaWikiImageView imageView = convertView.findViewById(R.id.categoryImageView); + TextView fileName = convertView.findViewById(R.id.categoryImageTitle); + TextView author = convertView.findViewById(R.id.categoryImageAuthor); + fileName.setText(item.getFilename()); + setAuthorView(item, author); + imageView.setMedia(item); + return convertView; + } + + /** + * Shows author information if its present + * @param item + * @param author + */ + private void setAuthorView(Media item, TextView author) { + if (item.getCreator() != null && !item.getCreator().equals("")) { + String uploadedByTemplate = context.getString(R.string.image_uploaded_by); + author.setText(String.format(uploadedByTemplate, item.getCreator())); + } else { + author.setVisibility(View.GONE); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java b/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java new file mode 100644 index 000000000..e12d5a778 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/category/QueryContinue.java @@ -0,0 +1,24 @@ +package fr.free.nrw.commons.category; + +/** + * For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages + * https://www.mediawiki.org/wiki/API:Raw_query_continue + */ +public class QueryContinue { + private String continueParam; + private String gcmContinueParam; + + public QueryContinue(String continueParam, String gcmContinueParam) { + this.continueParam = continueParam; + this.gcmContinueParam = gcmContinueParam; + } + + public String getGcmContinueParam() { + return gcmContinueParam; + } + + public String getContinueParam() { + return continueParam; + } +} + diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java index f88f3b34a..51aa85903 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java @@ -7,7 +7,7 @@ import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.auth.SignupActivity; import fr.free.nrw.commons.contributions.ContributionsActivity; -import fr.free.nrw.commons.featured.FeaturedImagesActivity; +import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.settings.SettingsActivity; @@ -49,5 +49,5 @@ public abstract class ActivityBuilderModule { abstract NotificationActivity bindNotificationActivity(); @ContributesAndroidInjector - abstract FeaturedImagesActivity bindFeaturedImagesActivity(); + abstract CategoryImagesActivity bindFeaturedImagesActivity(); } 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 e8b915c7e..55281be7e 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 @@ -6,6 +6,8 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v4.util.LruCache; +import com.google.gson.Gson; + import javax.inject.Named; import javax.inject.Singleton; @@ -85,6 +87,17 @@ public class CommonsApplicationModule { return context.getSharedPreferences("prefs", MODE_PRIVATE); } + /** + * + * @param context + * @return returns categoryPrefs + */ + @Provides + @Named("category_prefs") + public SharedPreferences providesCategorySharedPreferences(Context context) { + return context.getSharedPreferences("categoryPrefs", MODE_PRIVATE); + } + @Provides @Named("direct_nearby_upload_prefs") public SharedPreferences providesDirectNearbyUploadPreferences(Context context) { @@ -106,8 +119,11 @@ public class CommonsApplicationModule { @Provides @Singleton - public MediaWikiApi provideMediaWikiApi(Context context, @Named("default_preferences") SharedPreferences sharedPreferences) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, sharedPreferences); + public MediaWikiApi provideMediaWikiApi(Context context, + @Named("default_preferences") SharedPreferences defaultPreferences, + @Named("category_prefs") SharedPreferences categoryPrefs, + Gson gson) { + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); } @Provides @@ -116,6 +132,16 @@ public class CommonsApplicationModule { return new LocationServiceManager(context); } + /** + * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. + * @return returns a singleton Gson instance + */ + @Provides + @Singleton + public Gson provideGson() { + return new Gson(); + } + @Provides @Singleton public CacheController provideCacheController() { diff --git a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java index c5cdcb5a7..dfed64871 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/FragmentBuilderModule.java @@ -4,7 +4,7 @@ import dagger.Module; import dagger.android.ContributesAndroidInjector; import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.contributions.ContributionsListFragment; -import fr.free.nrw.commons.featured.FeaturedImagesListFragment; +import fr.free.nrw.commons.category.CategoryImagesListFragment; import fr.free.nrw.commons.media.MediaDetailFragment; import fr.free.nrw.commons.media.MediaDetailPagerFragment; import fr.free.nrw.commons.nearby.NearbyListFragment; @@ -49,6 +49,6 @@ public abstract class FragmentBuilderModule { abstract SingleUploadFragment bindSingleUploadFragment(); @ContributesAndroidInjector - abstract FeaturedImagesListFragment bindFeaturedImagesListFragment(); + abstract CategoryImagesListFragment bindFeaturedImagesListFragment(); } diff --git a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java deleted file mode 100644 index 853fba29e..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImage.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.free.nrw.commons.featured; - - -import fr.free.nrw.commons.Media; - -/** - * Object to hold FeaturedImage - */ - -public class FeaturedImage { - private Media image; - private String author; - private String fileName; - - public FeaturedImage(Media image, String author, String fileName) { - this.image = image; - this.author = author; - this.fileName = fileName; - } - - public Media getImage() { - return image; - } - - public void setImage(Media image) { - this.image = image; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public String getFileName() { - return fileName; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java deleted file mode 100644 index a2dc7b00b..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesActivity.java +++ /dev/null @@ -1,114 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.database.DataSetObserver; -import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.view.View; -import android.widget.AdapterView; - -import butterknife.ButterKnife; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.auth.AuthenticatedActivity; -import fr.free.nrw.commons.media.MediaDetailPagerFragment; - -/** - * This activity displays pic of the days of last xx days - */ - -public class FeaturedImagesActivity - extends AuthenticatedActivity - implements FragmentManager.OnBackStackChangedListener, - MediaDetailPagerFragment.MediaDetailProvider, - AdapterView.OnItemClickListener{ - - private FeaturedImagesListFragment featuredImagesListFragment; - private MediaDetailPagerFragment mediaDetails; - - @Override - protected void onAuthCookieAcquired(String authCookie) { - - } - - @Override - protected void onAuthFailure() { - - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_featured_images); - ButterKnife.bind(this); - - // Activity can call methods in the fragment by acquiring a - // reference to the Fragment from FragmentManager, using findFragmentById() - FragmentManager supportFragmentManager = getSupportFragmentManager(); - featuredImagesListFragment = (FeaturedImagesListFragment)supportFragmentManager - .findFragmentById(R.id.featuedListFragment); - - supportFragmentManager.addOnBackStackChangedListener(this); - if (savedInstanceState != null) { - mediaDetails = (MediaDetailPagerFragment)supportFragmentManager - .findFragmentById(R.id.featuredFragmentContainer); - - } - requestAuthToken(); - initDrawer(); - setTitle(getString(R.string.title_activity_featured_images)); - } - - @Override - public void onBackStackChanged() { - - } - - @Override - public void onItemClick(AdapterView adapterView, View view, int i, long l) { - if (mediaDetails == null || !mediaDetails.isVisible()) { - // set isFeaturedImage true for featured images, to include author field on media detail - mediaDetails = new MediaDetailPagerFragment(false, true); - FragmentManager supportFragmentManager = getSupportFragmentManager(); - supportFragmentManager - .beginTransaction() - .replace(R.id.featuredFragmentContainer, mediaDetails) - .addToBackStack(null) - .commit(); - supportFragmentManager.executePendingTransactions(); - } - mediaDetails.showImage(i); - } - - @Override - public Media getMediaAtPosition(int i) { - if (featuredImagesListFragment.getAdapter() == null) { - // not yet ready to return data - return null; - } else { - return ((FeaturedImage)featuredImagesListFragment.getAdapter().getItem(i)).getImage(); - } - } - - @Override - public int getTotalMediaCount() { - if (featuredImagesListFragment.getAdapter() == null) { - return 0; - } - return featuredImagesListFragment.getAdapter().getCount(); - } - - @Override - public void notifyDatasetChanged() { - - } - - @Override - public void registerDataSetObserver(DataSetObserver observer) { - - } - - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java deleted file mode 100644 index 19e33b0ee..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/FeaturedImagesListFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.GridView; -import android.widget.ListAdapter; - -import java.util.ArrayList; - -import butterknife.ButterKnife; -import dagger.android.support.DaggerFragment; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; - -public class FeaturedImagesListFragment extends DaggerFragment { - private GridView gridView; - private MockGridViewAdapter gridAdapter; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_featured_images, container, false); - ButterKnife.bind(this, v); - return v; - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - gridView = getView().findViewById(R.id.featuredImagesList); - gridView.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity()); - gridAdapter = new MockGridViewAdapter(this.getContext(), R.layout.layout_featured_images, getMockFeaturedImages()); - gridView.setAdapter(gridAdapter); - - } - - private ArrayList getMockFeaturedImages(){ - ArrayList featuredImages = new ArrayList<>(); - for (int i=0; i<10; i++){ - featuredImages.add(new FeaturedImage(new Media("test.jpg"), "username: test", "test file name")); - } - return featuredImages; - } - - public ListAdapter getAdapter() { - return gridView.getAdapter(); - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java deleted file mode 100644 index 7aa2a8892..000000000 --- a/app/src/main/java/fr/free/nrw/commons/featured/MockGridViewAdapter.java +++ /dev/null @@ -1,50 +0,0 @@ -package fr.free.nrw.commons.featured; - -import android.app.Activity; -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -import java.util.ArrayList; - -import fr.free.nrw.commons.MediaWikiImageView; -import fr.free.nrw.commons.R; - -/** - * This is created to only display UI implementation. Needs to be changed in real implementation - */ - -public class MockGridViewAdapter extends ArrayAdapter { - private Context context; - private int layoutResourceId; - private ArrayList data = new ArrayList(); - - public MockGridViewAdapter(Context context, int layoutResourceId, ArrayList data) { - super(context, layoutResourceId, data); - this.layoutResourceId = layoutResourceId; - this.context = context; - this.data = data; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - if (convertView == null) { - LayoutInflater inflater = ((Activity) context).getLayoutInflater(); - convertView = inflater.inflate(R.layout.layout_featured_images, null); - } - - FeaturedImage item = data.get(position); - MediaWikiImageView imageView = convertView.findViewById(R.id.featuredImageView); - TextView fileName = convertView.findViewById(R.id.featuredImageTitle); - TextView author = convertView.findViewById(R.id.featuredImageAuthor); - fileName.setText("Test file name"); - author.setText("Uploaded by: Test user name"); - imageView.setMedia(item.getImage()); - return convertView; - } - -} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 037f92e56..c3a977ab5 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -45,6 +45,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.ui.widget.CompatTextView; import timber.log.Timber; +import static android.view.View.*; import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailFragment extends CommonsDaggerSupportFragment { @@ -154,9 +155,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { authorLayout = (LinearLayout) view.findViewById(R.id.authorLinearLayout); if (isFeaturedMedia){ - authorLayout.setVisibility(View.VISIBLE); + authorLayout.setVisibility(VISIBLE); } else { - authorLayout.setVisibility(View.GONE); + authorLayout.setVisibility(GONE); } licenseList = new LicenseList(getActivity()); @@ -306,6 +307,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { } rebuildCatList(); + if(media.getCreator() == null || media.getCreator().equals("")) { + authorLayout.setVisibility(GONE); + } else { + author.setText(media.getCreator()); + } + checkDeletion(media); } @@ -313,13 +320,17 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (licenseLink(media) != null) { license.setOnClickListener(v -> openWebBrowser(licenseLink(media))); } else { - Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); - toast.show(); + if(isFeaturedMedia) { + Timber.d("Unable to fetch license URL for %s", media.getLicense()); + } else { + Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); + toast.show(); + } } if (media.getCoordinates() != null) { coordinates.setOnClickListener(v -> openMap(media.getCoordinates())); } - if (delete.getVisibility() == View.VISIBLE) { + if (delete.getVisibility() == VISIBLE) { enableDeleteButton(true); delete.setOnClickListener(v -> { @@ -369,7 +380,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); }); } - if (nominatedforDeletion.getVisibility() == View.VISIBLE){ + if (nominatedforDeletion.getVisibility() == VISIBLE){ seeMore.setOnClickListener(v -> { openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); }); @@ -476,12 +487,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private void checkDeletion(Media media){ if (media.getRequestedDeletion()){ - delete.setVisibility(View.GONE); - nominatedforDeletion.setVisibility(View.VISIBLE); + delete.setVisibility(GONE); + nominatedforDeletion.setVisibility(VISIBLE); } else{ - delete.setVisibility(View.VISIBLE); - nominatedforDeletion.setVisibility(View.GONE); + delete.setVisibility(VISIBLE); + nominatedforDeletion.setVisibility(GONE); } } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 78051abd8..6629d0933 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -9,6 +9,8 @@ import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.Log; +import com.google.gson.Gson; + import org.apache.http.HttpResponse; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; @@ -38,7 +40,10 @@ import java.util.Locale; import java.util.concurrent.Callable; import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.Media; import fr.free.nrw.commons.PageTitle; +import fr.free.nrw.commons.category.CategoryImageUtils; +import fr.free.nrw.commons.category.QueryContinue; import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.NotificationUtils; import in.yuvi.http.fluent.Http; @@ -46,6 +51,8 @@ import io.reactivex.Observable; import io.reactivex.Single; import timber.log.Timber; +import static fr.free.nrw.commons.utils.ContinueUtils.getQueryContinue; + /** * @author Addshore */ @@ -56,9 +63,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { private AbstractHttpClient httpClient; private MWApi api; private Context context; - private SharedPreferences sharedPreferences; + private SharedPreferences defaultPreferences; + private SharedPreferences categoryPreferences; + private Gson gson; - public ApacheHttpClientMediaWikiApi(Context context, String apiURL, SharedPreferences sharedPreferences) { + public ApacheHttpClientMediaWikiApi(Context context, + String apiURL, + SharedPreferences defaultPreferences, + SharedPreferences categoryPreferences, + Gson gson) { this.context = context; BasicHttpParams params = new BasicHttpParams(); SchemeRegistry schemeRegistry = new SchemeRegistry(); @@ -69,7 +82,9 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent()); httpClient = new DefaultHttpClient(cm, params); api = new MWApi(apiURL, httpClient); - this.sharedPreferences = sharedPreferences; + this.defaultPreferences = defaultPreferences; + this.categoryPreferences = categoryPreferences; + this.gson = gson; } @Override @@ -160,7 +175,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } private void setAuthCookieOnLogin(boolean isLoggedIn) { - SharedPreferences.Editor editor = sharedPreferences.edit(); + SharedPreferences.Editor editor = defaultPreferences.edit(); if (isLoggedIn) { editor.putBoolean("isUserLoggedIn", true); editor.putString("getAuthCookie", api.getAuthCookie()); @@ -448,6 +463,81 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { return NotificationUtils.getNotificationsFromList(context, childNodes); } + /** + * The method takes categoryName as input and returns a List of Media objects + * It uses the generator query API to get the images in a category, 10 at a time. + * Uses the query continue values for fetching paginated responses + * @param categoryName Category name as defined on commons + * @return + */ + @Override + @NonNull + public List getCategoryImages(String categoryName) { + ApiResult apiResult = null; + try { + MWApi.RequestBuilder requestBuilder = api.action("query") + .param("generator", "categorymembers") + .param("format", "xml") + .param("gcmtype", "file") + .param("gcmtitle", categoryName) + .param("prop", "imageinfo") + .param("gcmlimit", "10") + .param("iiprop", "url|extmetadata"); + + QueryContinue queryContinueValues = getQueryContinueValues(categoryName); + if (queryContinueValues != null) { + requestBuilder.param("continue", queryContinueValues.getContinueParam()); + requestBuilder.param("gcmcontinue", queryContinueValues.getGcmContinueParam()); + } + + apiResult = requestBuilder.get(); + } catch (IOException e) { + Timber.e("Failed to obtain searchCategories", e); + } + + if (apiResult == null) { + return new ArrayList<>(); + } + + ApiResult categoryImagesNode = apiResult.getNode("/api/query/pages"); + if (categoryImagesNode == null + || categoryImagesNode.getDocument() == null + || categoryImagesNode.getDocument().getChildNodes() == null + || categoryImagesNode.getDocument().getChildNodes().getLength() == 0) { + return new ArrayList<>(); + } + + QueryContinue queryContinue = getQueryContinue(apiResult.getNode("/api/continue").getDocument()); + setQueryContinueValues(categoryName, queryContinue); + + NodeList childNodes = categoryImagesNode.getDocument().getChildNodes(); + return CategoryImageUtils.getMediaList(childNodes); + } + + /** + * For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages + * https://www.mediawiki.org/wiki/API:Raw_query_continue + * After fetching images a page of image for a particular category, shared prefs are updated with the latest QueryContinue Values + * @param keyword + * @param queryContinue + */ + private void setQueryContinueValues(String keyword, QueryContinue queryContinue) { + SharedPreferences.Editor editor = categoryPreferences.edit(); + editor.putString(keyword, gson.toJson(queryContinue)); + editor.apply(); + } + + /** + * Before making a paginated API call, this method is called to get the latest query continue values to be used + * @param keyword + * @return + */ + @Nullable + private QueryContinue getQueryContinueValues(String keyword) { + String queryContinueString = categoryPreferences.getString(keyword, null); + return gson.fromJson(queryContinueString, QueryContinue.class); + } + @Override public boolean existingFile(String fileSha1) throws IOException { return api.action("query") diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index fd213455d..c0bd2fd87 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.List; +import fr.free.nrw.commons.Media; import fr.free.nrw.commons.notification.Notification; import io.reactivex.Observable; import io.reactivex.Single; @@ -34,6 +35,8 @@ public interface MediaWikiApi { boolean logEvents(LogBuilder[] logBuilders); + List getCategoryImages(String categoryName); + @NonNull UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, ProgressListener progressListener) throws IOException; diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index acd9b7646..4a7322b57 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -23,12 +23,11 @@ import fr.free.nrw.commons.AboutActivity; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.contributions.ContributionsActivity; -import fr.free.nrw.commons.featured.FeaturedImagesActivity; +import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.nearby.NearbyActivity; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.settings.SettingsActivity; @@ -37,6 +36,8 @@ import timber.log.Timber; public abstract class NavigationBaseActivity extends BaseActivity implements NavigationView.OnNavigationItemSelectedListener { + private static final String FEATURED_IMAGES_CATEGORY = "Category:Featured_pictures_on_Wikimedia_Commons"; + @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.navigation_view) @@ -157,7 +158,7 @@ public abstract class NavigationBaseActivity extends BaseActivity return true; case R.id.action_featured_images: drawerLayout.closeDrawer(navigationView); - startActivityWithFlags(this, FeaturedImagesActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_featured_images), FEATURED_IMAGES_CATEGORY); return true; default: Timber.e("Unknown option [%s] selected from the navigation menu", itemId); diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java new file mode 100644 index 000000000..b05c8bc45 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/ContinueUtils.java @@ -0,0 +1,15 @@ +package fr.free.nrw.commons.utils; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import fr.free.nrw.commons.category.QueryContinue; + +public class ContinueUtils { + + public static QueryContinue getQueryContinue(Node document) { + Element continueElement = (Element) document; + return new QueryContinue(continueElement.getAttribute("continue"), + continueElement.getAttribute("gcmcontinue")); + } +} diff --git a/app/src/main/res/layout/activity_featured_images.xml b/app/src/main/res/layout/activity_category_images.xml similarity index 69% rename from app/src/main/res/layout/activity_featured_images.xml rename to app/src/main/res/layout/activity_category_images.xml index 755fe4983..c329e4458 100644 --- a/app/src/main/res/layout/activity_featured_images.xml +++ b/app/src/main/res/layout/activity_category_images.xml @@ -1,6 +1,5 @@ - - diff --git a/app/src/main/res/layout/fragment_featured_images.xml b/app/src/main/res/layout/fragment_category_images.xml similarity index 70% rename from app/src/main/res/layout/fragment_featured_images.xml rename to app/src/main/res/layout/fragment_category_images.xml index ca45f44c3..001f0a780 100644 --- a/app/src/main/res/layout/fragment_featured_images.xml +++ b/app/src/main/res/layout/fragment_category_images.xml @@ -1,20 +1,21 @@ - + android:background="?attr/mainBackground"> @@ -33,7 +33,7 @@ android:padding="@dimen/small_gap" > Proceed Cancel Retry + + No images found! + Error occurred while loading images. + Uploaded by: %1$s Share App diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 73760dd40..b1de29143 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -3,6 +3,7 @@ package fr.free.nrw.commons import android.content.Context import android.content.SharedPreferences import android.support.v4.util.LruCache +import com.google.gson.Gson import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil @@ -36,6 +37,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val accountUtil: AccountUtil = mock() val appSharedPreferences: SharedPreferences = mock() val defaultSharedPreferences: SharedPreferences = mock() + val categorySharedPreferences: SharedPreferences = mock() val otherSharedPreferences: SharedPreferences = mock() val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() @@ -45,6 +47,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() + val gson: Gson = Gson() override fun providesAccountUtil(context: Context): AccountUtil = accountUtil @@ -58,7 +61,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager - override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences): MediaWikiApi = mediaWikiApi + override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences, categorySharedPreferences: SharedPreferences, gson: Gson): MediaWikiApi = mediaWikiApi override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager 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 c51d354c2..686a90ef2 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 @@ -3,6 +3,7 @@ package fr.free.nrw.commons.mwapi import android.content.SharedPreferences import android.os.Build import android.preference.PreferenceManager +import com.google.gson.Gson import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.TestCommonsApplication import okhttp3.mockwebserver.MockResponse @@ -26,12 +27,14 @@ class ApacheHttpClientMediaWikiApiTest { private lateinit var testObject: ApacheHttpClientMediaWikiApi private lateinit var server: MockWebServer private lateinit var sharedPreferences: SharedPreferences + private lateinit var categoryPreferences: SharedPreferences @Before fun setUp() { server = MockWebServer() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) - testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences) + categoryPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) + testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences, categoryPreferences, Gson()) testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/") } From f28cc6fc8ce9c900ba92c94f2d821d18110dedb2 Mon Sep 17 00:00:00 2001 From: Josephine Lim Date: Mon, 7 May 2018 19:27:14 +1000 Subject: [PATCH 19/94] Update pull_request_template.md (#1476) * Update pull_request_template.md * Remove Javadocs mention * Added required/optional notes --- PULL_REQUEST_TEMPLATE.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 34078f07e..9d7150008 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,15 +1,15 @@ -## Description +## Description (required) -Fixes #{GitHub issue number} +Fixes #{GitHub issue number and title} {Describe the changes made and why they were made.} -## Tests performed +## Tests performed (required) Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdDebug}. -{Please test your PR at least once before submitting.} - -## Screenshots showing what changed +## Screenshots showing what changed (optional) {Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)} + +_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._ From 539c03bf047f039ae501d00262cdef3b8b028c6f Mon Sep 17 00:00:00 2001 From: Man Parvesh Singh Randhawa Date: Mon, 7 May 2018 17:32:11 +0800 Subject: [PATCH 20/94] resolves #1464 : MediaDataExtractor is making inefficient (redundant) server calls (#1496) --- app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java index 2d79a6c4f..affb57528 100644 --- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java @@ -61,8 +61,8 @@ public class MediaDataExtractor { } try{ - Timber.d("Nominated for deletion: " + mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename)); - deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/"+filename); + deletionStatus = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); + Timber.d("Nominated for deletion: " + deletionStatus); } catch (Exception e){ Timber.d(e.getMessage()); From aca3f0f832370e1859892c4c89604595fcd84ccd Mon Sep 17 00:00:00 2001 From: Tanvi Dadu Date: Mon, 7 May 2018 15:27:59 +0530 Subject: [PATCH 21/94] Open map of place where picture was taken (#1360) * Intent to map added * Merge conflicts resolved * Added the functionality to hide FAB incase of null coordinate * Merge Conflict resolved * Improve pr quality * Improve Quality * Added nested FAB animations * Nested FAB implemented * Improve Quality * Added up arrow * Javadocs Added --- .../nrw/commons/upload/ShareActivity.java | 105 +++++++++++++++++- .../ic_keyboard_arrow_up_black_24dp.xml | 5 + app/src/main/res/layout/activity_share.xml | 27 ++++- app/src/main/res/values/dimens.xml | 2 + app/src/main/res/values/strings.xml | 2 + 5 files changed, 135 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 77f76fbed..6a59c8e30 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -81,8 +81,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; - - +import android.support.design.widget.FloatingActionButton; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; import static java.lang.Long.min; @@ -122,6 +121,7 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; private SimpleDraweeView backgroundImageView; + private FloatingActionButton maps_fragment; private boolean cacheFound; @@ -145,6 +145,8 @@ public class ShareActivity private long ShortAnimationDuration; private FloatingActionButton zoomInButton; private FloatingActionButton zoomOutButton; + private FloatingActionButton mainFab; + private boolean isFABOpen = false; /** @@ -284,6 +286,24 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } + + mainFab = (FloatingActionButton) findViewById(R.id.main_fab); + /* + * called when upper arrow floating button + */ + mainFab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if(!isFABOpen){ + showFABMenu(); + }else{ + closeFABMenu(); + } + } + }); + + + zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); try { zoomInButton.setOnClickListener(new View.OnClickListener() { @@ -358,7 +378,74 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); + maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); + maps_fragment.setVisibility(View.VISIBLE); + if( imageObj == null || imageObj.imageCoordsExists != true){ + maps_fragment.setVisibility(View.INVISIBLE); + } + + + maps_fragment.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if( imageObj != null && imageObj.imageCoordsExists == true) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); + mapIntent.setPackage("com.google.android.apps.maps"); + startActivity(mapIntent); + } + } + }); } + /* + * Function to display the zoom and map FAB + */ + private void showFABMenu(){ + isFABOpen=true; + + if( imageObj != null && imageObj.imageCoordsExists == true) + maps_fragment.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.VISIBLE); + + mainFab.animate().rotationBy(180); + maps_fragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); + zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); + } + + /* + * function to close the zoom and map FAB + */ + private void closeFABMenu(){ + isFABOpen=false; + mainFab.animate().rotationBy(-180); + maps_fragment.animate().translationY(0); + zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animator) { + + } + + @Override + public void onAnimationEnd(Animator animator) { + if(!isFABOpen){ + maps_fragment.setVisibility(View.GONE); + zoomInButton.setVisibility(View.GONE); + } + + } + + @Override + public void onAnimationCancel(Animator animator) { + + } + + @Override + public void onAnimationRepeat(Animator animator) { + + } + }); + } + @Override public void onRequestPermissionsResult(int requestCode, @@ -461,6 +548,9 @@ public class ShareActivity detectUnwantedPicturesAsync.execute(); } + /* + * to display permission snackbar in share activity + */ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { @@ -693,7 +783,9 @@ public class ShareActivity return super.onOptionsItemSelected(item); } - // Get SHA1 of file from input stream + /* + * Get SHA1 of file from input stream + */ private String getSHA1(InputStream is) { MessageDigest digest; @@ -730,6 +822,9 @@ public class ShareActivity } } + /* + * function to provide pinch zoom + */ private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { // If there's an animation in progress, cancel it // immediately and proceed with this one. @@ -737,6 +832,8 @@ public class ShareActivity CurrentAnimator.cancel(); } ViewUtil.hideKeyboard(ShareActivity.this.findViewById(R.id.titleEdit | R.id.descEdit)); + closeFABMenu(); + mainFab.setVisibility(View.GONE); InputStream input = null; Bitmap scaled = null; try { @@ -866,7 +963,7 @@ public class ShareActivity CurrentAnimator.cancel(); } zoomOutButton.setVisibility(View.GONE); - zoomInButton.setVisibility(View.VISIBLE); + mainFab.setVisibility(View.VISIBLE); // Animate the four positioning/sizing properties in parallel, // back to their original values. diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml new file mode 100644 index 000000000..bc010396b --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_up_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml index ca8097495..b6e523239 100644 --- a/app/src/main/res/layout/activity_share.xml +++ b/app/src/main/res/layout/activity_share.xml @@ -41,8 +41,6 @@ - - + + + + + + + 20sp 16sp 14sp + 15dp + 25dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3e83fefd1..00bfafdad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -275,4 +275,6 @@ Error occurred while loading images. Uploaded by: %1$s Share App + Coordinates were not specified during image selection + From 8eed630ee57036dcd07ced2fba98dee8c9a5e857 Mon Sep 17 00:00:00 2001 From: neslihanturan Date: Mon, 7 May 2018 15:05:20 +0300 Subject: [PATCH 22/94] Add nearby tutorial (#1467) * Add dependency for MaterialShowcase * Add actionview class to get a reference to material showcase * Create a NearbyMaterialShowcaseSequence class * Apply sequence steps * Add first three steps of nearby showcase * Add sequence id constants to make sure they will be displayed only once * Add last step of sequence to explain plus fab * Create an object to prevent customize all sequences every time * Fix typo * Code cleanup * Add strings to strings.xml * Code cleanup * Revert irrelevant change * Revert irrelevant change * Remove showcaseview for recenter button * Use single showcaseView instead of sequence * Add single showcase view insted of sequence to be able to edit text style * Make sure it will be displayed only once * Cleanup * Update strings * Change dismiss text style --- app/build.gradle | 2 + .../nrw/commons/nearby/NearbyActivity.java | 91 ++++++++++++++++++- .../nrw/commons/nearby/NearbyMapFragment.java | 32 ++++++- .../NearbyMaterialShowcaseSequence.java | 18 ++++ .../fr/free/nrw/commons/utils/ViewUtil.java | 4 + app/src/main/res/values/strings.xml | 6 ++ 6 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java diff --git a/app/build.gradle b/app/build.gradle index 9c6f62fd4..5d37f8f54 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,6 +25,8 @@ dependencies { transitive=true } + implementation "com.github.deano2390:MaterialShowcaseView:1.2.0" + implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION" implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION" implementation "com.android.support:design:$SUPPORT_LIB_VERSION" diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 2a423de93..3bf8beacf 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -4,14 +4,18 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Typeface; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; + import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -25,6 +29,7 @@ import com.google.gson.GsonBuilder; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import butterknife.BindView; import butterknife.ButterKnife; @@ -41,6 +46,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.IShowcaseListener; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener { @@ -56,12 +63,15 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp LinearLayout bottomSheetDetails; @BindView(R.id.transparentView) View transparentView; + @BindView(R.id.fab_recenter) + View fabRecenter; @Inject LocationServiceManager locationManager; @Inject NearbyController nearbyController; - + @Inject + @Named("application_preferences") SharedPreferences applicationPrefs; private LatLng curLatLng; private Bundle bundle; private Disposable placesDisposable; @@ -72,11 +82,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp private NearbyListFragment nearbyListFragment; private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName(); private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName(); + private View listButton; // Reference to list button to use in tutorial private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; private BroadcastReceiver broadcastReceiver; + + private boolean isListShowcaseAdded = false; + private boolean isMapShowCaseAdded = false; + private LatLng lastKnownLocation; + private MaterialShowcaseView secondSingleShowCaseView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -126,6 +143,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_nearby, menu); + new Handler().post(() -> { + + listButton = findViewById(R.id.action_display_list); + + secondSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(listButton) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_list_icon)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_1) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + // If dismissed, we can inform fragment to start showcase sequence there + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + nearbyMapFragment.onNearbyMaterialShowcaseDismissed(); + } + }) + .build(); + + isListShowcaseAdded = true; + + if (isMapShowCaseAdded) { // If map showcase is also ready, start ShowcaseSequence + // Probably this case is not possible. Just added to be careful + setMapViewTutorialShowCase(); + } + }); + return super.onCreateOptionsMenu(menu); } @@ -420,6 +470,45 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp updateMapFragment(false); updateListFragment(); } + + isMapShowCaseAdded = true; + } + + public void setMapViewTutorialShowCase() { + /* + *This showcase view will be the first step of our nearbyMaterialShowcaseSequence. The reason we use a + * single item instead of adding another step to nearbyMaterialShowcaseSequence is that we are not able to + * call withoutShape() method on steps. For mapView we need an showcase view without + * any circle on it, it should cover the whole page. + * */ + MaterialShowcaseView firstSingleShowCaseView = new MaterialShowcaseView.Builder(this) + .setTarget(nearbyMapFragment.mapView) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_whole_nearby_activity)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_2) // provide a unique ID used to ensure it is only shown once + .withoutShape() // no shape on map view since there are no view to focus on + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .setListener(new IShowcaseListener() { + @Override + public void onShowcaseDisplayed(MaterialShowcaseView materialShowcaseView) { + + } + + @Override + public void onShowcaseDismissed(MaterialShowcaseView materialShowcaseView) { + /* Add other nearbyMaterialShowcaseSequence here, it will make the user feel as they are a + * nearbyMaterialShowcaseSequence whole together. + * */ + secondSingleShowCaseView.show(NearbyActivity.this); + } + }) + .build(); + + if (applicationPrefs.getBoolean("firstRunNearby", true)) { + applicationPrefs.edit().putBoolean("firstRunNearby", false).apply(); + firstSingleShowCaseView.show(this); + } } private void lockNearbyView(boolean lock) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index 431132436..69041d286 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; @@ -58,13 +59,14 @@ import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.utils.UriDeserializer; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; import static android.app.Activity.RESULT_OK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; public class NearbyMapFragment extends DaggerFragment { - private MapView mapView; + public MapView mapView; private List baseMarkerOptions; private fr.free.nrw.commons.location.LatLng curLatLng; public fr.free.nrw.commons.location.LatLng[] boundaryCoordinates; @@ -111,6 +113,10 @@ public class NearbyMapFragment extends DaggerFragment { private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.06; private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.04; + private boolean isSecondMaterialShowcaseDismissed; + private boolean isMapReady; + private MaterialShowcaseView thirdSingleShowCaseView; + private Bundle bundleForUpdtes;// Carry information from activity about changed nearby places and current location @Inject @@ -163,7 +169,6 @@ public class NearbyMapFragment extends DaggerFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - Timber.d("onCreateView called"); if (curLatLng != null) { Timber.d("curLatLng found, setting up map view..."); @@ -476,6 +481,7 @@ public class NearbyMapFragment extends DaggerFragment { mapView.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(MapboxMap mapboxMap) { + ((NearbyActivity)getActivity()).setMapViewTutorialShowCase(); NearbyMapFragment.this.mapboxMap = mapboxMap; updateMapSignificantly(); } @@ -519,6 +525,7 @@ public class NearbyMapFragment extends DaggerFragment { private void addNearbyMarkerstoMapBoxMap() { mapboxMap.addMarkers(baseMarkerOptions); + mapboxMap.setOnInfoWindowCloseListener(marker -> { if (marker == selected) { bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); @@ -534,6 +541,7 @@ public class NearbyMapFragment extends DaggerFragment { }); mapboxMap.setOnMarkerClickListener(marker -> { + if (marker instanceof NearbyMarker) { this.selected = marker; NearbyMarker nearbyMarker = (NearbyMarker) marker; @@ -541,6 +549,7 @@ public class NearbyMapFragment extends DaggerFragment { passInfoToSheet(place); bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } return false; }); @@ -634,7 +643,19 @@ public class NearbyMapFragment extends DaggerFragment { addAnchorToSmallFABs(fabGallery, getActivity().findViewById(R.id.empty_view).getId()); addAnchorToSmallFABs(fabCamera, getActivity().findViewById(R.id.empty_view1).getId()); + thirdSingleShowCaseView = new MaterialShowcaseView.Builder(this.getActivity()) + .setTarget(fabPlus) + .setDismissText(getString(R.string.showcase_view_got_it_button)) + .setContentText(getString(R.string.showcase_view_plus_fab)) + .setDelay(500) // optional but starting animations immediately in onCreate can make them choppy + .singleUse(ViewUtil.SHOWCASE_VIEW_ID_3) // provide a unique ID used to ensure it is only shown once + .setDismissStyle(Typeface.defaultFromStyle(Typeface.BOLD)) + .build(); + isMapReady = true; + if (isSecondMaterialShowcaseDismissed) { + thirdSingleShowCaseView.show(getActivity()); + } } @@ -791,6 +812,13 @@ public class NearbyMapFragment extends DaggerFragment { this.bundleForUpdtes = bundleForUpdtes; } + public void onNearbyMaterialShowcaseDismissed() { + isSecondMaterialShowcaseDismissed = true; + if (isMapReady) { + thirdSingleShowCaseView.show(getActivity()); + } + } + @Override public void onStart() { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java new file mode 100644 index 000000000..c6e46611d --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMaterialShowcaseSequence.java @@ -0,0 +1,18 @@ +package fr.free.nrw.commons.nearby; + +import android.app.Activity; + +import uk.co.deanwild.materialshowcaseview.MaterialShowcaseSequence; +import uk.co.deanwild.materialshowcaseview.ShowcaseConfig; + + +public class NearbyMaterialShowcaseSequence extends MaterialShowcaseSequence { + + public NearbyMaterialShowcaseSequence(Activity activity, String sequenceID) { + super(activity, sequenceID); + ShowcaseConfig config = new ShowcaseConfig(); + config.setDelay(500); // half second between each showcase view + this.setConfig(config); + this.singleUse(sequenceID); // Display tutorial only once + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java index 1c45e8178..0c22a40a2 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ViewUtil.java @@ -10,6 +10,10 @@ import android.widget.Toast; public class ViewUtil { + public static final String SHOWCASE_VIEW_ID_1 = "SHOWCASE_VIEW_ID_1"; + public static final String SHOWCASE_VIEW_ID_2 = "SHOWCASE_VIEW_ID_2"; + public static final String SHOWCASE_VIEW_ID_3 = "SHOWCASE_VIEW_ID_3"; + public static void showSnackbar(View view, int messageResourceId) { Snackbar.make(view, messageResourceId, Snackbar.LENGTH_SHORT).show(); } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 00bfafdad..dcea51bcc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -271,9 +271,15 @@ Cancel Retry + Got it! + These are the places near you that need pictures to illustrate their Wikipedia articles + Tapping this button brings up a list of these places + You can upload a picture for any place from your gallery or camera + No images found! Error occurred while loading images. Uploaded by: %1$s + Share App Coordinates were not specified during image selection From 1ae28e37c32da3aa553d1c36655210fd30f1f98d Mon Sep 17 00:00:00 2001 From: Kaartic Sivaraam Date: Tue, 8 May 2018 06:34:09 +0000 Subject: [PATCH 23/94] CONTRIBUTING: fix formatting of the gist of the guidelines (#1453) * CONTRIBUTING: fix formatting of the gist of the guidelines First level headings for a gist seems to be overkill. So, replace first level headings with an ordered-list which sounds more meaningful. * CONTRIBUTING: specify clearly that 'blame' is a feature of "Git" The contributing file specifies about the ability to know who wrote something without the need of @author javadoc tags but incorrectly attributes the feature to GitHub. Correctly attribute the feature to where it belongs, Git, and specify the name of the feature to help users easily take advantage of it. --- CONTRIBUTING.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5a8e7af19..caa02a103 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,23 +5,30 @@ If you're not sure where to start head on to [this wiki page](https://github.com Here's a gist of the guidelines, -# Make separate commits for logically separate changes +1. Make separate commits for logically separate changes -# Describe your changes well in the commit message +1. Describe your changes well in the commit message -The first line of the commit message should be a short description of what has + The first line of the commit message should be a short description of what has changed. It is also good to prefix the first line with "area: " where the "area" is a filename or identifier for the general area of the code being modified. The body should provide a meaningful commit message. -# Write Javadocs +1. Write Javadocs -We require contributors to include Javadocs for all new methods and classes submitted via PRs (after 1 May 2018). This is aimed at making it easier for new contributors to dive into our codebase, especially those who are new to Android development. A few things to note: + We require contributors to include Javadocs for all new methods and classes + submitted via PRs (after 1 May 2018). This is aimed at making it easier for + new contributors to dive into our codebase, especially those who are new to + Android development. A few things to note: -- This should not replace the need for code that is easily-readable in and of itself -- Please make sure that your Javadocs are reasonably descriptive, not just a copy of the method name -- Please do not use `@author` tags - we aim for collective code ownership, and if needed, GitHub allows us to see who wrote something without needing to add these tags + - This should not replace the need for code that is easily-readable in + and of itself + - Please make sure that your Javadocs are reasonably descriptive, not just + a copy of the method name + - Please do not use `@author` tags - we aim for collective code ownership, + and if needed, Git allows us to see who wrote something without needing + to add these tags (`git blame`) -# Write tests for your code (if possible) +1. Write tests for your code (if possible) -# Make sure the Wiki pages don't become stale by updating them (if needed) +1. Make sure the Wiki pages don't become stale by updating them (if needed) From cd212b7daa96960c5aa70fbd7b0088a40897b345 Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Tue, 8 May 2018 12:21:13 +0530 Subject: [PATCH 24/94] Feature/switch to butterknife (#1494) * Implemented butterknife in MediaDetailFragment [issue #1491] * Implemented butterknife in MediaDetailPagerFragment [[issue #1491]] * post merge upstream master wip [[issue #1491]] --- .../commons/media/MediaDetailFragment.java | 200 ++++++++++-------- .../media/MediaDetailPagerFragment.java | 7 +- 2 files changed, 112 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index c3a977ab5..9614c4f00 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -9,6 +9,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.Nullable; import android.text.Editable; +import android.text.TextUtils; import android.text.TextWatcher; import android.util.TypedValue; import android.view.LayoutInflater; @@ -22,6 +23,9 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -45,7 +49,8 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.ui.widget.CompatTextView; import timber.log.Timber; -import static android.view.View.*; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailFragment extends CommonsDaggerSupportFragment { @@ -75,23 +80,37 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Inject MediaWikiApi mwApi; - - private MediaWikiImageView image; - private MediaDetailSpacer spacer; private int initialListTop = 0; - private TextView title; - private TextView desc; - private TextView author; - private TextView license; - private TextView coordinates; - private TextView uploadedDate; - private TextView seeMore; - private LinearLayout nominatedforDeletion; - private LinearLayout categoryContainer; - private LinearLayout authorLayout; - private Button delete; - private ScrollView scrollView; + @BindView(R.id.mediaDetailImage) + MediaWikiImageView image; + @BindView(R.id.mediaDetailSpacer) + MediaDetailSpacer spacer; + @BindView(R.id.mediaDetailTitle) + TextView title; + @BindView(R.id.mediaDetailDesc) + TextView desc; + @BindView(R.id.mediaDetailAuthor) + TextView author; + @BindView(R.id.mediaDetailLicense) + TextView license; + @BindView(R.id.mediaDetailCoordinates) + TextView coordinates; + @BindView(R.id.mediaDetailuploadeddate) + TextView uploadedDate; + @BindView(R.id.seeMore) + TextView seeMore; + @BindView(R.id.nominatedDeletionBanner) + LinearLayout nominatedForDeletion; + @BindView(R.id.mediaDetailCategoryContainer) + LinearLayout categoryContainer; + @BindView(R.id.authorLinearLayout) + LinearLayout authorLayout; + @BindView(R.id.nominateDeletion) + Button delete; + @BindView(R.id.mediaDetailScrollView) + ScrollView scrollView; + private ArrayList categoryNames; private boolean categoriesLoaded = false; private boolean categoriesPresent = false; @@ -101,6 +120,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private AsyncTask detailFetchTask; private LicenseList licenseList; + //Had to make this class variable, to implement various onClicks, which access the media, also I fell why make separate variables when one can serve the purpose + private Media media; + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -137,22 +159,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { final View view = inflater.inflate(R.layout.fragment_media_detail, container, false); - image = (MediaWikiImageView) view.findViewById(R.id.mediaDetailImage); - scrollView = (ScrollView) view.findViewById(R.id.mediaDetailScrollView); - - // Detail consists of a list view with main pane in header view, plus category list. - spacer = (MediaDetailSpacer) view.findViewById(R.id.mediaDetailSpacer); - title = (TextView) view.findViewById(R.id.mediaDetailTitle); - desc = (TextView) view.findViewById(R.id.mediaDetailDesc); - author = (TextView) view.findViewById(R.id.mediaDetailAuthor); - license = (TextView) view.findViewById(R.id.mediaDetailLicense); - coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates); - uploadedDate = (TextView) view.findViewById(R.id.mediaDetailuploadeddate); - seeMore = (TextView) view.findViewById(R.id.seeMore); - nominatedforDeletion = (LinearLayout) view.findViewById(R.id.nominatedDeletionBanner); - delete = (Button) view.findViewById(R.id.nominateDeletion); - categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer); - authorLayout = (LinearLayout) view.findViewById(R.id.authorLinearLayout); + ButterKnife.bind(this,view); if (isFeaturedMedia){ authorLayout.setVisibility(VISIBLE); @@ -196,7 +203,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { @Override public void onResume() { super.onResume(); - Media media = detailProvider.getMediaAtPosition(index); + media = detailProvider.getMediaAtPosition(index); if (media == null) { // Ask the detail provider to ping us when we're ready Timber.d("MediaDetailFragment not yet ready to display details; registering observer"); @@ -209,17 +216,18 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { Timber.d("MediaDetailFragment ready to display delayed details!"); detailProvider.unregisterDataSetObserver(dataObserver); dataObserver = null; - displayMediaDetails(detailProvider.getMediaAtPosition(index)); + media=detailProvider.getMediaAtPosition(index); + displayMediaDetails(); } }; detailProvider.registerDataSetObserver(dataObserver); } else { Timber.d("MediaDetailFragment ready to display details"); - displayMediaDetails(media); + displayMediaDetails(); } } - private void displayMediaDetails(final Media media) { + private void displayMediaDetails() { //Always load image from Internet to allow viewing the desc, license, and cats image.setMedia(media); @@ -256,7 +264,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (success) { extractor.fill(media); setTextFields(media); - setOnClickListeners(media); } else { Timber.d("Failed to load photo details."); } @@ -316,74 +323,81 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { checkDeletion(media); } - private void setOnClickListeners(final Media media) { - if (licenseLink(media) != null) { - license.setOnClickListener(v -> openWebBrowser(licenseLink(media))); + @OnClick(R.id.mediaDetailLicense) + public void onMediaDetailLicenceClicked(){ + if (!TextUtils.isEmpty(licenseLink(media))) { + openWebBrowser(licenseLink(media)); } else { if(isFeaturedMedia) { - Timber.d("Unable to fetch license URL for %s", media.getLicense()); + Timber.d("Unable to fetch license URL for %s", media.getLicense()); } else { Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); toast.show(); } } + } + + @OnClick(R.id.mediaDetailCoordinates) + public void onMediaDetailCoordinatesClicked(){ if (media.getCoordinates() != null) { - coordinates.setOnClickListener(v -> openMap(media.getCoordinates())); + openMap(media.getCoordinates()); } - if (delete.getVisibility() == VISIBLE) { - enableDeleteButton(true); + } - delete.setOnClickListener(v -> { + @OnClick(R.id.nominateDeletion) + public void onDeleteButtonClicked(){ + //Reviewer correct me if i have misunderstood something over here + //But how does this if (delete.getVisibility() == View.VISIBLE) { + // enableDeleteButton(true); makes sense ? + AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); + alert.setMessage("Why should this file be deleted?"); + final EditText input = new EditText(getActivity()); + alert.setView(input); + input.requestFocus(); + alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + String reason = input.getText().toString(); + DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); + deleteTask.execute(); + enableDeleteButton(false); + } + }); + alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + } + }); + AlertDialog d = alert.create(); + input.addTextChangedListener(new TextWatcher() { + private void handleText() { + final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); + if (input.getText().length() == 0) { + okButton.setEnabled(false); + } else { + okButton.setEnabled(true); + } + } - AlertDialog.Builder alert = new AlertDialog.Builder(getActivity()); - alert.setMessage("Why should this file be deleted?"); - final EditText input = new EditText(getActivity()); - alert.setView(input); - input.requestFocus(); - alert.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - String reason = input.getText().toString(); - DeleteTask deleteTask = new DeleteTask(getActivity(), media, reason); - deleteTask.execute(); - enableDeleteButton(false); - } - }); - alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int whichButton) { - } - }); - AlertDialog d = alert.create(); - input.addTextChangedListener(new TextWatcher() { - private void handleText() { - final Button okButton = d.getButton(AlertDialog.BUTTON_POSITIVE); - if (input.getText().length() == 0) { - okButton.setEnabled(false); - } else { - okButton.setEnabled(true); - } - } + @Override + public void afterTextChanged(Editable arg0) { + handleText(); + } - @Override - public void afterTextChanged(Editable arg0) { - handleText(); - } + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + d.show(); + d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); + } - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - }); - d.show(); - d.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false); - }); - } - if (nominatedforDeletion.getVisibility() == VISIBLE){ - seeMore.setOnClickListener(v -> { - openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); - }); + @OnClick(R.id.seeMore) + public void onSeeMoreClicked(){ + if(nominatedForDeletion.getVisibility()== VISIBLE) { + openWebBrowser(media.getFilePageTitle().getMobileUri().toString()); } } @@ -488,11 +502,11 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { private void checkDeletion(Media media){ if (media.getRequestedDeletion()){ delete.setVisibility(GONE); - nominatedforDeletion.setVisibility(VISIBLE); + nominatedForDeletion.setVisibility(VISIBLE); } else{ delete.setVisibility(VISIBLE); - nominatedforDeletion.setVisibility(GONE); + nominatedForDeletion.setVisibility(GONE); } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index c0564c603..62d1261cf 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -26,6 +26,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import butterknife.BindView; +import butterknife.ButterKnife; import javax.inject.Inject; import javax.inject.Named; @@ -53,7 +55,8 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple @Named("default_preferences") SharedPreferences prefs; - private ViewPager pager; + @BindView(R.id.mediaDetailsPager) + ViewPager pager; private Boolean editable; private boolean isFeaturedImage; @@ -72,7 +75,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false); - pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager); + ButterKnife.bind(this,view); pager.addOnPageChangeListener(this); final MediaDetailAdapter adapter = new MediaDetailAdapter(getChildFragmentManager()); From d012572b926afe709f08cefaf341c7fb55926c30 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 10 May 2018 10:11:14 +0200 Subject: [PATCH 25/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 1 + app/src/main/res/values-bn/strings.xml | 3 + app/src/main/res/values-de/strings.xml | 8 +++ app/src/main/res/values-diq/strings.xml | 8 +++ app/src/main/res/values-el/strings.xml | 8 +++ app/src/main/res/values-es/strings.xml | 8 +++ app/src/main/res/values-eu/strings.xml | 17 +++-- app/src/main/res/values-fr/strings.xml | 9 +++ app/src/main/res/values-it/strings.xml | 7 +++ app/src/main/res/values-iw/strings.xml | 8 +++ app/src/main/res/values-ja/strings.xml | 73 +++++++++++++++++++--- app/src/main/res/values-ko/strings.xml | 8 +++ app/src/main/res/values-lb/strings.xml | 4 ++ app/src/main/res/values-lv/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 8 +++ app/src/main/res/values-pl/strings.xml | 2 + app/src/main/res/values-pms/strings.xml | 8 +++ app/src/main/res/values-pt-rBR/strings.xml | 8 +++ app/src/main/res/values-pt/strings.xml | 19 ++++-- app/src/main/res/values-ru/strings.xml | 9 +++ app/src/main/res/values-skr/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 20 ++++-- app/src/main/res/values-zh-rTW/strings.xml | 10 ++- app/src/main/res/values-zh/strings.xml | 8 +++ 24 files changed, 231 insertions(+), 25 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 6d9c94c1d..a9127c1eb 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -145,4 +145,5 @@ مرحبا بكم في ويكيمديا كومنز، %1$s! نحن سعداء لأنك هنا. %1$s رسالة على صفحة الحديث %1$s ذكر لك على %2$s. + شارك التطبيق diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 679c50803..e7167393c 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -259,4 +259,7 @@ ইন্টারনেট উপলব্ধ কোন বিজ্ঞপ্তি পাওয়া যায়নি পুনঃচেষ্টা করুন + বুঝেছি! + কোন চিত্র পাওয়া যায়নি! + আপলোড করেছেন: %1$s diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 36e013976..5f751153c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -269,5 +269,13 @@ Fortfahren Abbrechen Erneut versuchen + Verstanden! + Dies sind die Orte in deiner Nähe, die Bilder zur Illustration ihrer Wikipedia-Artikel benötigen. + Das Antippen dieser Schaltfläche zeigt eine Liste mit diesen Orten + Du kannst ein Bild für einen beliebigen Ort von deiner Galerie oder Kamera hochladen + Keine Bilder gefunden! + Beim Laden der Bilder ist ein Fehler aufgetreten. + Hochgeladen von: %1$s App teilen + Während der Bildauswahl wurden keine Koordinaten angegeben diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 2b66709f0..1f1d69ea3 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -9,11 +9,15 @@ * Mirzali --> + Asayış + Bıngeh + Lokasyon Commons Eyari Namey karberi Parola Cı kewe + Parola, xo vira kerde? Qeyd be Cıkewtış Kerem kerên, bıpawên... @@ -58,6 +62,8 @@ Bar ke Kategoriyan dı cı geyr Star ke + Newe ke + Liste \@string/contributions_subtitle_zero Yew barbiyayış @@ -129,5 +135,7 @@ Keye Bar ke Veciyayış + Bıtexelne Anciya bıcerrebne + Mı fehm kerd! diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0dae5c65e..ee50aa3d7 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -273,5 +273,13 @@ Συνέχεια Ακύρωση Ξαναπροσπαθήστε + Κατάλαβα! + Αυτά είναι τα μέρη κοντά σας που χρειάζονται φωτογραφίες για να εικονογραφηθούν τα λήμματά τους στη Βικιπαίδεια + Πατώντας αυτό το κουμπί φέρνει μια λίστα αυτών των μερών + Μπορείτε να ανεβάσετε μια εικόνα για οποιοδήποτε μέρος από την γκαλερί ή την κάμερά σας + Δεν βρέθηκαν εικόνες! + Συνέβη σφάλμα κατά το ανέβασμα των εικόνων. + Ανέβηκε από: %1$s Κοινοποίηση εφαρμογής + Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index c1a45b309..69cf2f8b7 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -267,4 +267,12 @@ Selecciona el idioma en que quieres enviar traducciones Cancelar Reintentar + Entendido + Estos sitios cercanos a ti necesitan imágenes para ilustrar sus artículos de Wikipedia + Puedes cargar una imagen para cualquier sitio desde la galería o la cámara + No se encontró ninguna imagen. + Se produjo un error al cargar las imágenes. + Cargada por: %1$s + Compartir aplicación + No se especificaron las coordenadas al seleccionar la imagen diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index f44262e05..a109ff541 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -8,11 +8,17 @@ * Theklan --> + Itxura + Orokorra + Feedback + Kokapena Commons + Hobespenak Erabiltzaile izena Pasahitza Saioa hasi + Pasahitza ahaztu duzu? Eman izena Saioa hasten Mesedez itxaron… @@ -54,22 +60,23 @@ Kategoriak bilatu Gorde Eguneratu + Zerrenda GPSa gaitu Oraindik ez da ezer igo - - Oraindik igoerarik ez + + \@string/contributions_subtitle_zero igoera 1 %1$d igoera Ez da kategoriak aukritu %1$s izenarekin - Gehitu kategoriak zure argazkiak Wikimedia Commonsen aurkitzen errazagoak izan daitezen. + Gehitu kategoriak zure argazkiak Wikimedia Commonsen aurkitzen errazagoak izan daitezen.\nHasi idazten kategoriak gehitzeko. Kategoriak Hobespenak Eman izena Honi buruz Open Source softwarea <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache v2 Lizentziaren</a> pean egina. Wikimedia Commons eta bere logoa Wikimedia Fundazioaren marka erregistratuak dira eta Wikimedia Fundazioaren baimenarekin erabiltzen dira. Ez gaude Wikimedia Fundaziora afiliatuta. GitHub-eko <a href=\"https://github.com/commons-app/apps-android-commons\">Iturria</a> eta <a href=\"https://commons-app.github.io/\">webgunea</a>. <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-eko gai</a> berria sortu erroreen berri emateko. - <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Pribatutasun politika</a> + <u>Pribatutasun politika</u> Honi buruz Bidali zure iritzia (e-posta bidez) Posta bezerorik ez da instalatu @@ -81,7 +88,7 @@ Irudi hau %1$s lizentziapean egongo da Irudi hau bidaltzen, nire lan propioa dela aitortzen dut, copyrighta duten materiala edo selfiak ez duela, eta beste motatakoak <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Ohikoaren arauak</a> Jaitsi - Lizentzia + Berezko lizentzia Aurreko izenburu/deskribapena erabili Oraingo kokapena automatikoki lortu Gau modua diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 442fc6bc5..6dd08a30d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -5,6 +5,7 @@ * Happy13241 * Jean-Frédéric * KATRINE1992 +* Melissadeba95 * Metroitendo * Nicolas Raoul * Orikrin1998 @@ -279,5 +280,13 @@ Continuer Annuler Réessayer + C’est bon ! + Il y a des lieux autour de vous qui demandent des images pour illustrer leurs articles Wikipédia + En cliquant sur ce bouton vous afficherez une liste de ces endroits + Vous pouvez téléverser une photo de n\'importe quel endroit de votre gallerie ou de votre appareil photo + Aucune images trouvée. + Une erreur s\'est produite pendant le chargement des images. + Importé par:%1$s Partager les applications + Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 306a1e3a4..06bfa701f 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -223,4 +223,11 @@ Lingue Annulla Riprova + Capito! + Questi sono i luoghi vicino a te che necessitano di immagini per illustrare le loro voci di Wikipedia + Puoi caricare un\'immagine per ogni luogo dalla tua galleria o fotocamera + Nessuna immagine trovata! + Si è verificato un errore durante il caricamento delle immagini. + Caricato da: %1$s + Condividi applicazione diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 762a3be31..f0cdf1f3d 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -273,5 +273,13 @@ המשך ביטול לנסות שוב + הבנתי! + אלה המקומות בסביבתך שזקוקים לתמונות כדי להמחיש את הערכים שלהם בוויקיפדיה + ניתן ללחוץ על כפתור זה כדי להציג רשימה של המקומות האלה + באפשרותך להעלות תמונה של כל מקום מהגלריה או מהמצלמה שלך + לא נמצאו תמונות! + אירעה שגיאה בטעינת התמונות. + הועלתה על־ידי: %1$s שיתוף היישום + לא צוינו קואורדינטות בעת בחירת התמונה diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e34c98432..ef45a5d3a 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -12,13 +12,18 @@ * Yusuke1109 --> + 表示 + 全般 フィードバック 場所 コモンズ + 設定 利用者名 パスワード + コモンズのベータ版アカウントにログイン ログイン + パスワードを忘れた場合 利用者登録 ログイン中 お待ちください… @@ -49,6 +54,7 @@ 共有 ブラウザーで表示 タイトル + ファイル名をつけてください 説明 ログインできません - ネットワークのエラーです ログインできません - 利用者名を確認してください @@ -64,6 +70,7 @@ カテゴリを検索 保存 更新 + 一覧 お使いのデバイスではGPSが無効になっています。有効にしますか? GPSを有効にする まだ何もアップロードされていません。 @@ -82,6 +89,7 @@ カテゴリ 設定 利用者登録 + 秀逸な画像 このアプリについて ウィキメディア・コモンズ・アプリはウィキメディア・コミュニティの助成金受給者とボランティアによって製作・メンテナンスされているオープンソースソフトウェアです。ウィキメディア財団はこのアプリの製作・開発・メンテナンスに関与していません。 バグとアイディアは <a href=\"https://github.com/commons-app/apps-android-commons/issues\">Github</a> へ。 @@ -96,6 +104,7 @@ 再試行 キャンセル この画像が %1$s ライセンスでアップロードされます。 + この画像の投稿に当たり、私はこれが自分自身の作品であり、著作権のあるコンテンツや自撮りは含まれていないと宣言します。 ダウンロード 既定のライセンス 前回のタイトルと記述を使用 @@ -126,11 +135,19 @@ ウィキメディア・コモンズにはウィキペディアで使用する画像のほぼすべてが保管されています。 あなたの画像は世界中の人々が学習する助けになります! アップロードする画像はあなたご本人が撮影したものかあなたが単独で制作したものに限定します。 - - 自然物 (動植物、山)\n- 道具 (自転車、駅)\n- 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) + 自然物 (動植物、山)\n• 道具 (自転車、駅)\n• 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) + 自然物 (動植物、山) + 道具 (自転車、駅) + 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) アップロードが《禁止》のもの: - あなたの友人の自撮り写真や画像\n- インターネットからダウンロードした画像\n- 著作権のあるアプリのスクリーンショット + 自撮りもしくは友達を撮影した写真 + ウェブからダウンロードした画像 + 独自のアプリケーションのスクリーンショット アップロードの例: - 題名: シドニー・オペラハウス\n- 説明: 湾の向こうから見たシドニー・オペラハウス\n- カテゴリ: 西側から見たシドニー・オペラハウス、遠くから見たシドニー・オペラハウス + 題名: シドニーのオペラハウス + 説明: シドニーのオペラハウス。湾を挟んで撮影。 画像を投稿してください。ウィキペディアの記事に彩りを! ウィキペディアの画像はウィキメディア・コモンズに保管されています。 あなたの画像は世界中の人々が学習する助けになります @@ -143,11 +160,11 @@ 説明はありません。 不明なライセンス 更新 - 必要な権限:外部ストレージを読み込みます。これがなければアプリは機能しません。 - 必要な権限:外部ストレージを作成します。これがなければアプリは機能しません。 - オプションの権限:カテゴリ候補の現在の位置を取得する + 必要な権限:外部ストレージを読み込みます。これがなければアプリはギャラリーを開けません。 + 必要な権限:外部ストレージに入力します。これがないとアプリはカメラにアクセスできません。 + オプションの権限:カテゴリ候補のため現在の位置を取得する 承認 - 周りの場所 + 近くの場所 付近の場所が見つかりません 警告 このファイルが既にコモンズにあります。本当にアップロードしますか? @@ -158,6 +175,7 @@ 記述 ここにメディアの説明が入ります。かなり長文になる場合には数行にわたることがあります。それでも見栄えがよいと願っています。 作者 + 秀逸な画像の作者名を記入します。 アップロード日時 ライセンス 緯度経度 @@ -174,14 +192,19 @@ コモンズの商標 コモンズのウェブサイト コモンズのフェイスブックページ + コモンズのGithubソースコード 背景画像 + メディアイメージが失敗しました 画像がありません 画像をアップロード 蔵王連峰 リャマ レインボーブリッジ チューリップ + 自撮りはアップロードできません + 独自の著作権がある画像 ウィキペディアへようこそ + 著作権について シドニーオペラハウス キャンセル 開く @@ -195,28 +218,62 @@ ログアウト チュートリアル 通知 - 場所の権限がないと、近くの場所を表示できません + 秀逸 + 場所の権限がないため、近くの場所を表示できません 説明がありません + コモンズのファイルページ ウィキデータ項目 ウィキペディアの記事 画像をキャッシュする際のエラー ファイル固有の説明的な表題。ファイル名として使われます。平易な言葉を使い、空白を入れることができます。拡張子は含めないでください。 可能な限りメディアを説明してください:どこで撮られましたか?それは何を示していますか?文脈とは何ですか?物や人を説明してください。容易に推測できない情報、例えば風景の場合の時刻を明らかにする。メディアに珍しいことがある場合は、何が珍しいのかを説明してください。 - 権限を取得 + この画像は暗すぎますがアップロードしますか? ウィキメディア・コモンズは百科事典に適した画像のみ受け付けます。 + ピントが合っていませんが、アップロードしますか? ウィキメディア・コモンズは百科事典に適した画像のみ受け付けます。 + 権限を付与 外部ストレージを使用 アプリ内のカメラで撮影した写真を端末に保存する 自分のアカウントにログイン ログファイルを送信する メールで開発者にログファイルを送信する + URLを開くブラウザーが見つかりません + エラーが発生しました。URL が見つかりません + 削除の提案 + この画像の削除が提案されています。 ブラウザーで表示 場所は変更されていません。 位置が無効です。 + 近くの場所を表示するには権限が必要です + 道順を調べる 記事を読む + ウィキメディアコモンズにようこそ、%1$さん! このサイトへ来てくれてありがとうございます。 + %1$さんからアナタのとーくぺ^字にメッセージが届いています + 編集をしてくれてありがとうございます + %1$さんが%2$であなたに言及しています。 + 表示の切り替え + 道順 + ウィキデータ + ウィキペディア + コモンズ <u>評価する</u> <u>FAQ</u> チュートリアルをスキップする + インターネットに接続していません + インターネットに接続しました + 通知の取得に失敗しました + 通知はありません <u>翻訳</u> 言語 + どの言語に編集するか選択 + 次へ キャンセル - 再試行 + やり直す + 了解 + 近くでウィキペディアの記事に使う写真がない場所はこちら + このボタンをタップするとリストを表示します + 場所の写真をアップロードするには、ギャラリーから選ぶことも撮影することもできます + 画像がありません + 画像の読み込み中にエラーが発生しました + アップロードした人: %1$ + アプリをシェアする + 画像の選択中に位置情報を特定できませんでした diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d0e1c21ed..e80999d54 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -266,5 +266,13 @@ 진행 취소 다시 시도 + 알겠습니다! + 이들은 위키백과 글에 사진을 넣을 필요가 있는 당신 주위의 장소들입니다 + 이 버튼을 탭하면 이 장소들의 목록을 가져옵니다 + 갤러리나 카메라 어느 곳이든 사진을 올릴 수 있습니다 + 그림이 없습니다! + 그림을 불러오는 동안 오류가 발생했습니다. + 올린이: %1$s 앱 공유 + 그림 선택 중에 좌표가 지정되지 않았습니다 diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index 5abca6562..d346b90bd 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -240,4 +240,8 @@ Virufueren Ofbriechen Nach eng Kéier probéieren + Verstanen! + Keng Biller fonnt! + Feeler beim Eropluede vu Biller. + Eropgeluede vum: %1$s diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 1eba570d2..b6c5103e2 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -109,4 +109,5 @@ Valodas Turpināt Atcelt + Sapratu! diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 4e44dad60..492e1bc0c 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -263,5 +263,13 @@ Продолжи Откажи Пробај пак + Јасно! + Ова се места во ваша близинана кои им требаат слики за илустрирање на нивните статии на Википедија + Ако допрете на копчево ќе добиете список на тие места + Можете да подигнете слика за било кое од местата од вашата галерија или камера + Не пронајдов ниедна слика! + Се појави грешка при вчитувањето на сликите. + Подигач: %1$s Сподели прилог + Не беа укажани координати при изборот на сликата diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 77cdb547d..ccc5f374f 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -227,4 +227,6 @@ Nie znaleziono powiadomień Języki Anuluj + Nie znaleziono grafik! + Wystąpił błąd podczas ładowania grafik. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 30115f1ad..53ce91cc5 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -263,5 +263,13 @@ Andé anans Anulé Prové torna + Fàit! + A-i é dij pòst davzin a chiel ch\'a l\'han da manca ëd plance për ilustré ij sò artìcoj su Wikipedia + Sgnacand su \'s boton a comparirà na lista ëd si pòst + A peul carié na fòto da \'n pòst qualsëssìa ëd soa galarìa o màchina fòto + Gnun-e plance trovà! + A-i é staje n\'eror durant ël cariament ëd le plance. + Carià da: %1$s Partagé j\'aplicassion + Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 6722ae4bb..23b5e4abf 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -275,5 +275,13 @@ Avançar Cancelar Tentar novamente + Entendido! + Estes são os lugares perto de você que precisam de fotografias para ilustrar os respetivos artigos na Wikipédia + Tocar neste botão fará surgir uma lista destes lugares + Pode carregar uma fotografia para qualquer dos lugares, da sua galeria ou câmara + Não foi encontrada nenhuma imagem! + Ocorreu um erro durante o carregamento das imagens. + Carregada por: %1$s Compartilhar o aplicativo + Não foram especificadas coordenadas durante a seleção da imagem diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b0717f3dc..3da1296b7 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -16,12 +16,12 @@ Geral Comentários Localização - Wikimedia Commons + Commons Configurações - Nome de utilizador(a) + Nome de utilizador Palavra-passe - Entre com a sua conta do Commons Beta + Entre com a sua conta da wiki Commons Beta Iniciar sessão Esqueceu-se da palavra-passe? Registar-se @@ -118,7 +118,7 @@ Utilizar tema escuro Atribuição-CompartilhaIgual 4.0 Atribuição 4.0 - Atribuição – Compartilhamento pela mesma Licença + Atribuição–CompartilhaIgual 3.0 Atribuição 3.0 CC0 CC BY-SA 3.0 @@ -136,7 +136,7 @@ CC-BY-SA 4.0 CC BY 4.0 CC Zero - Wikimedia Commons armazena a maioria das imagens que são usadas na Wikipédia. + A wiki Wikimedia Commons aloja a maioria das imagens que são usadas na Wikipédia. As suas imagens ajudam a educar pessoas em todo o mundo! Por favor, carregue apenas imagens tiradas ou criadas exclusivamente por si: Objetos naturais (flores, animais, montanhas)\n• Objetos úteis (bicicletas, estações de comboio)\n• Pessoas famosas (o seu presidente da câmara, atletas olímpicos que conheça) @@ -274,4 +274,13 @@ Avançar Cancelar Tentar novamente + Entendido! + Estes são os lugares perto de si que precisam de fotografias para ilustrar os respetivos artigos na Wikipédia + Tocar neste botão fará surgir uma lista destes lugares + Pode carregar uma fotografia para qualquer dos lugares, da sua galeria ou câmara + Não foi encontrada nenhuma imagem! + Ocorreu um erro durante o carregamento das imagens. + Carregada por: %1$s + Partilhar aplicação + Não foram especificadas coordenadas durante a seleção da imagem diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4dc90489d..ce71af94b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -285,4 +285,13 @@ Выполняется Отмена Повторить + Понятно! + Это места поблизости, статьи о которых нуждаются в иллюстрациях + Нажатие этой кнопки сгенерирует список таких мест + Вы можете загрузить изображение для любого из этих мест, сделав снимок камерой или выбрав уже существующее изображение из галереи + Изображений не найдено! + Произошла ошибка при загрузке изображений. + Загружено участником %1$s + Поделиться приложением + Во время выбора изображения не были указаны координаты diff --git a/app/src/main/res/values-skr/strings.xml b/app/src/main/res/values-skr/strings.xml index 796ae8555..73a5b7e51 100644 --- a/app/src/main/res/values-skr/strings.xml +++ b/app/src/main/res/values-skr/strings.xml @@ -129,4 +129,5 @@ اڳوں تے تھیوو منسوخ ولدا کوشش کرو + گھن گھندا diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 24ef60b61..17a324a97 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -90,9 +90,9 @@ Kategorier Inställningar Registrera - Utvalda bild + Utvalda bilder Om - Wikimedia Commons är en app med öppen källkod som skapas och underhålls av frivilliga från Wikimedias gemenskap. Wikimedia Foundation är inte involverad i skapandet, utvecklingen eller underhållet av appen. + Wikimedia Commons-appen är en app med öppen källkod som skapas och underhålls av frivilliga från Wikimedias gemenskap. Wikimedia Foundation är inte involverad i skapandet, utvecklingen eller underhållet av appen. Skapa ett nytt <a href=\"https://github.com/commons-app/apps-android-commons/issues\">ärende på GitHub</a> för att rapportera buggar och förslag. <u>Integritetspolicy</u> <u>Erkännande</u> @@ -105,7 +105,7 @@ Försök igen Avbryt Denna bild kommer att licensieras under %1$s - Genom att skicka in denna bild intygar jag att detta är mitt eget verk, som inte innehåller upphovsrättsskyddat material eller selfies samt annars följer <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons-policys</a>. + Genom att skicka in denna bild intygar jag att detta är mitt eget verk, som inte innehåller upphovsrättsskyddat material eller selfies samt följer <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Wikimedia Commons-policys</a>. Ladda ned Standardlicens Använd föregående titel/beskrivning @@ -135,7 +135,7 @@ CC Zero Wikimedia Commons lagrar de flesta bilderna som används på Wikipedia. Dina bilder hjälper till att utbilda\nmänniskor runt hela världen! - Ladda upp bilder som endast du har tagit eller skapat: + Ladda endast upp bilder som du har tagit eller skapat själv: Naturliga föremål (blommor, djur, berg)\n• Användbara föremål (cyklar, tågstationer)\n• Berömda personer (din borgmästare, olympiska atleter du har träffat) Naturliga föremål (blommor, djur, berg) Användbar föremål (cyklar, tågstationer) @@ -163,8 +163,8 @@ Ingen beskrivning Okänd licens Uppdatera - Nödvändig behörighet: Läsa extern lagring. Appen kan inte komma åt ditt galleri utan detta. - Nödvändig behörighet: Skriva till extern lagring. Appen kan inte komma åt din kamera utan detta. + Nödvändig behörighet: Läs extern lagring. Appen kan inte komma åt ditt galleri utan detta. + Nödvändig behörighet: Skriv till extern lagring. Appen kan inte komma åt din kamera utan detta. Valfri behörighet: Hämta aktuell plats för kategoriförslag OK Platser i närheten @@ -271,5 +271,13 @@ Fortsätt Avbryt Försök igen + Uppfattat! + Detta är platserna nära dig som behöver bilder för att illustrera deras Wikipedia-artiklar + Klicka på den här knappen för att få upp en lista med dessa platser + Du kan ladda upp en bild från vilken plats som helst från ditt galleri eller kamera + Inga bilder hittades! + Ett fel uppstod vid inläsning av bilder. + Uppladdad av: %1$s Dela app + Koordinater specificerades inte vid bildvalet diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index a2878cddf..80b595f5d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -32,7 +32,7 @@ 未能核對身分! 開始上傳! 已上傳%1$s! - 點選檢視您上傳的項目 + 輕觸來檢視您上傳的項目 開始上傳%1$s 正在上傳%1$s 即將完成上傳 %1$s @@ -271,5 +271,13 @@ 已進行 取消 重試 + 了解! + 這些是在您的附近,並且需要圖片來圖解有關它們的維基百科條目之地點 + 輕觸此按鈕來帶出這些地點的清單 + 您可從您的圖庫或相機,來上傳任何地點的圖片 + 找不到圖片! + 當載入圖片時發生錯誤。 + 由:%1$s 上傳 分享應用程式 + 當選擇圖片時未指定座標 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index d3d049ea2..29a1ae898 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -269,5 +269,13 @@ 已处理 取消 重试 + 明白了! + 这些是您附近需要图片以阐明维基百科条目的地方 + 点按此按钮会出现这些地点的列表 + 您可以从您的图库或照相机中上传任意地点的图片 + 找不到图片! + 加载图片时出错。 + 由%1$s上传 分享应用 + 图片选择时,坐标并未指定 From 8bdc4f6b95a33ece82d68136446faa9c02d93a0a Mon Sep 17 00:00:00 2001 From: Ashish Kumar Date: Sat, 12 May 2018 16:49:43 +0530 Subject: [PATCH 26/94] Bug fix #1504 (#1506) * Bug fix #1504 * Filtered messages with ConnectException [issue #1504] * A generalised message for exceptions in Nearby Activity [issue #1504] --- .../nrw/commons/nearby/NearbyActivity.java | 29 ++++++++++++++++--- .../nrw/commons/nearby/NearbyController.java | 3 +- .../free/nrw/commons/nearby/NearbyPlaces.java | 10 +------ app/src/main/res/values/strings.xml | 1 + 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 3bf8beacf..04886fda4 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -16,6 +16,7 @@ import android.support.design.widget.BottomSheetBehavior; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -23,9 +24,14 @@ import android.view.View; import android.widget.LinearLayout; import android.widget.ProgressBar; +import android.widget.Toast; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.reactivex.functions.Consumer; +import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; import java.util.List; import javax.inject.Inject; @@ -427,8 +433,14 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); - } else if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); + } else if (locationChangeType + .equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { Gson gson = new GsonBuilder() .registerTypeAdapter(Uri.class, new UriSerializer()) .create(); @@ -451,7 +463,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp if (placeList.size() == 0) { ViewUtil.showSnackbar(findViewById(R.id.container), R.string.no_nearby); } - + bundle.putString("PlaceList", gsonPlaceList); //bundle.putString("CurLatLng", gsonCurLatLng); bundle.putString("BoundaryCoord", gsonBoundaryCoordinates); @@ -580,7 +592,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp .loadAttractionsFromLocation(curLatLng)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::populatePlaces); + .subscribe(this::populatePlaces, + throwable -> { + Timber.d(throwable); + showErrorMessage(getString(R.string.error_fetching_nearby_places)); + progressBar.setVisibility(View.GONE); + }); nearbyMapFragment.setBundleForUpdtes(bundle); nearbyMapFragment.updateMapSignificantly(); updateListFragment(); @@ -646,4 +663,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp public void prepareViewsForSheetPosition(int bottomSheetState) { // TODO } + + private void showErrorMessage(String message) { + ViewUtil.showLongToast(NearbyActivity.this, message); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index 015d22135..bd042b4d7 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -7,6 +7,7 @@ import android.support.graphics.drawable.VectorDrawableCompat; import com.mapbox.mapboxsdk.annotations.IconFactory; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -44,7 +45,7 @@ public class NearbyController { * @return NearbyPlacesInfo a variable holds Place list without distance information * and boundary coordinates of current Place List */ - public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) { + public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) throws IOException { Timber.d("Loading attractions near %s", curLatLng); NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index a2f4b2352..d05d81251 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -40,10 +40,9 @@ public class NearbyPlaces { } } - List getFromWikidataQuery(LatLng curLatLng, String lang) { + List getFromWikidataQuery(LatLng curLatLng, String lang) throws IOException { List places = Collections.emptyList(); - try { // increase the radius gradually to find a satisfactory number of nearby places while (radius <= MAX_RADIUS) { places = getFromWikidataQuery(curLatLng, lang, radius); @@ -54,13 +53,6 @@ public class NearbyPlaces { radius *= RADIUS_MULTIPLIER; } } - } catch (IOException e) { - Timber.d(e.toString()); - // errors tend to be caused by too many results (and time out) - // try a small radius next time - Timber.d("back to initial radius: %f", radius); - radius = INITIAL_RADIUS; - } // make sure we will be able to send at least one request next time if (radius > MAX_RADIUS) { radius = MAX_RADIUS; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dcea51bcc..6e30baa10 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -282,5 +282,6 @@ Share App Coordinates were not specified during image selection + Error fetching nearby places. From 2b867f0f9b22f802e56efe184468307669bda421 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 14 May 2018 08:13:34 +0200 Subject: [PATCH 27/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-bn/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-iw/strings.xml | 1 + app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-mk/strings.xml | 1 + app/src/main/res/values-pms/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-qq/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 9 +++++++++ app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 1 + 15 files changed, 23 insertions(+) diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index e7167393c..7c76ebaea 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -262,4 +262,5 @@ বুঝেছি! কোন চিত্র পাওয়া যায়নি! আপলোড করেছেন: %1$s + কাছাকাছি স্থানগুলি আনতে ত্রুটি। diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5f751153c..9f47ce42e 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -278,4 +278,5 @@ Hochgeladen von: %1$s App teilen Während der Bildauswahl wurden keine Koordinaten angegeben + Fehler beim Abrufen der Orte in der Nähe. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index ee50aa3d7..e372df836 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -282,4 +282,5 @@ Ανέβηκε από: %1$s Κοινοποίηση εφαρμογής Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας + Σφάλμα κατά την εύρεση κοντινών μερών. diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 69cf2f8b7..9e5dae650 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -275,4 +275,5 @@ Cargada por: %1$s Compartir aplicación No se especificaron las coordenadas al seleccionar la imagen + Error al recuperar los lugares cercanos. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6dd08a30d..bfb14baf1 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -289,4 +289,5 @@ Importé par:%1$s Partager les applications Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image + Erreur durant l\'exploration du voisinage. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index f0cdf1f3d..9ba67e4ee 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -282,4 +282,5 @@ הועלתה על־ידי: %1$s שיתוף היישום לא צוינו קואורדינטות בעת בחירת התמונה + שגיאה באחזור המקומות בסביבתך. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index e80999d54..883665c85 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -275,4 +275,5 @@ 올린이: %1$s 앱 공유 그림 선택 중에 좌표가 지정되지 않았습니다 + 주변 장소를 가져오는데 오류가 있습니다. diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 492e1bc0c..a344fcad2 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -272,4 +272,5 @@ Подигач: %1$s Сподели прилог Не беа укажани координати при изборот на сликата + Грешка при добивањето на околните места. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 53ce91cc5..8b2bd292c 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -272,4 +272,5 @@ Carià da: %1$s Partagé j\'aplicassion Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia + Eror durant l\'esplorassion dj\'anviron. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 23b5e4abf..849c4e6b1 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -284,4 +284,5 @@ Carregada por: %1$s Compartilhar o aplicativo Não foram especificadas coordenadas durante a seleção da imagem + Erro ao buscar lugares próximos. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 3da1296b7..e59582fa0 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,4 +283,5 @@ Carregada por: %1$s Partilhar aplicação Não foram especificadas coordenadas durante a seleção da imagem + Erro ao localizar locais próximos. diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index 37c2978bf..d84ce08ab 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -99,6 +99,7 @@ Message explaining what kind of images not to submit. Message asking user if they understand what kinds of images to upload. Button text for confirming the user understands what kinds of images to upload.\n{{Identical|Yes}} + \'\'This message is empty, and it\'s probably invalid. See bug report: https://github.com/commons-app/apps-android-commons/issues/1333 .\'\' Label for categories list in media detail panel.\n{{Identical|Category}} Placeholder for categories list in media detail panel, while loading from network.\n{{Identical|Loading}} Placeholder for categories list in media detail panel, if no categories found.\n{{Identical|None selected}} diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 95548b662..dadd40917 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -271,4 +271,13 @@ İlerle Vazgeç Tekrar Deneyin + Anladım! + Vikipedi maddelerine eklemek için fotoğrafa ihtiyaç duyan size yakın yerler + Bu tuşa dokunmak bu yerlerin bir listesini getirir + Galerinizden veya kameranızla herhangi bir yer için resim yükleyebilirsiniz. + Resim bulunamadı! + Resimler yüklenirken hata oluştu. + Yükleyen: %1$s + Uygulamayı Paylaş + Koordinatlar görüntü seçimi sırasında belirlenmedi diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 80b595f5d..4581d45ce 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -280,4 +280,5 @@ 由:%1$s 上傳 分享應用程式 當選擇圖片時未指定座標 + 索取附近地點時出錯。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 29a1ae898..2079eb925 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -278,4 +278,5 @@ 由%1$s上传 分享应用 图片选择时,坐标并未指定 + 检索附近地点时出错。 From f99363c06c8d9ea000a90421000c3760a9982892 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Tue, 15 May 2018 12:55:37 +0530 Subject: [PATCH 28/94] Fix security exception crash while accessing network location provider (#1498) * Fix security exception crash while accessing network location provider * Added java docs --- .../location/LocationServiceManager.java | 33 ++++++++++++---- .../nrw/commons/nearby/NearbyActivity.java | 39 +++++++++++++++++-- 2 files changed, 61 insertions(+), 11 deletions(-) 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 73ded852f..49c422633 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,6 +1,7 @@ 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; @@ -10,9 +11,10 @@ import android.location.LocationManager; import android.os.Bundle; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.util.Log; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import timber.log.Timber; @@ -29,6 +31,7 @@ public class LocationServiceManager implements LocationListener { private Location lastLocation; private final List locationListeners = new CopyOnWriteArrayList<>(); private boolean isLocationManagerRegistered = false; + private Set locationExplanationDisplayed = new HashSet<>(); /** * Constructs a new instance of LocationServiceManager. @@ -51,7 +54,6 @@ public class LocationServiceManager implements LocationListener { /** * Returns whether the location permission is granted. - * * @return true if the location permission is granted */ public boolean isLocationPermissionGranted() { @@ -73,10 +75,23 @@ public class LocationServiceManager implements LocationListener { 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) { - return !activity.isFinishing() && - ActivityCompat.shouldShowRequestPermissionRationale(activity, - Manifest.permission.ACCESS_FINE_LOCATION); + 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; } /** @@ -84,8 +99,9 @@ public class LocationServiceManager implements LocationListener { * (e.g. when Location permission just granted) * @return last known LatLng */ + @SuppressLint("MissingPermission") public LatLng getLKL() { - if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + if (isLocationPermissionGranted()) { Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (lastKL == null) { lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); @@ -107,9 +123,10 @@ public class LocationServiceManager implements LocationListener { * Registers a LocationManager to listen for current location. */ public void registerLocationManager() { - if (!isLocationManagerRegistered) + if (!isLocationManagerRegistered) { isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) && requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); + } } /** @@ -142,7 +159,7 @@ public class LocationServiceManager implements LocationListener { * @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly * LOCATION_SLIGHTLY_CHANGED if location changed slightly */ - protected LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { + private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { if (currentBestLocation == null) { // A new location is always better than no location diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 04886fda4..35e15b0d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -322,7 +322,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp protected void onStart() { super.onStart(); locationManager.addLocationListener(this); - locationManager.registerLocationManager(); + registerLocationUpdates(); } @Override @@ -400,7 +400,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp return; } - locationManager.registerLocationManager(); + registerLocationUpdates(); LatLng lastLocation = locationManager.getLastLocation(); if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed @@ -450,6 +450,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp } } + /** + * 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()) { + locationManager.registerLocationManager(); + } else { + // Should we show an explanation? + if (locationManager.isPermissionExplanationRequired(this)) { + new AlertDialog.Builder(this) + .setMessage(getString(R.string.location_permission_rationale_nearby)) + .setPositiveButton("OK", (dialog, which) -> { + requestLocationPermissions(); + dialog.dismiss(); + }) + .setNegativeButton("Cancel", (dialog, id) -> { + showLocationPermissionDeniedErrorDialog(); + dialog.cancel(); + }) + .create() + .show(); + + } else { + // No explanation needed, we can request the permission. + requestLocationPermissions(); + } + } + } else { + locationManager.registerLocationManager(); + } + } + private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) { List placeList = nearbyPlacesInfo.placeList; LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates; @@ -530,7 +563,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp locationManager.removeLocationListener(this); } else { lockNearbyView = false; - locationManager.registerLocationManager(); + registerLocationUpdates(); locationManager.addLocationListener(this); } } From 677f85a09703da359c33bd9189f339b1e5aed147 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 17 May 2018 08:24:27 +0200 Subject: [PATCH 29/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ast/strings.xml | 14 ++++++++++++++ app/src/main/res/values-diq/strings.xml | 8 ++++++-- app/src/main/res/values-hu/strings.xml | 12 ++++++++++-- app/src/main/res/values-is/strings.xml | 10 ++++++++++ app/src/main/res/values-ru/strings.xml | 19 ++++++++----------- app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 7 +++++++ 8 files changed, 57 insertions(+), 15 deletions(-) diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 532a4897e..947bc441a 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -84,6 +84,7 @@ Categoríes Configuración Date d\'alta + Imáxenes destacaes Tocante a La app de Wikimedia Commons ye software de códigu abiertu, creáu y calteníu por becaos y voluntarios de la comunidá de Wikimedia. La Fundación Wikimedia nun participa na creación, desendolcu nin caltenimientu de la app. Crea una nueva <a href=\"https://github.com/commons-app/apps-android-commons/issues\">incidencia en GitHub</a> pa informar de problemes y suxerencies. @@ -169,6 +170,8 @@ Títulu del mediu Descripción Equí va la descripción del mediu. Esto pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea bien. + Autor + El nome d\'usuariu del autor de la imaxe destacada va equí. Data d\'unviu Llicencia Coordenaes @@ -211,6 +214,7 @@ Salir Tutorial Avisos + Destacada Los sitios cercanos nun pueden amosase ensin los permisos d\'allugamientu nun s\'atoparon descripciones Páxina del ficheru en Commons @@ -259,4 +263,14 @@ Siguir Encaboxar Retentar + Entendílo + Estos son sitios cercanos a ti que precisen imaxes para ilustrar los sos artículos de Wikipedia + Tocando esti botón amuésase la llista d\'esos llugares + Puedes xubir una imaxe pa cualquier sitiu dende la galería o la cámara + Nun s\'alcontró nenguna imaxe + Asocedió un error al cargar les imáxenes. + Xubida por: %1$s + Compartir app + Nun s\'especificaron les coordenaes al escoyer la imaxe + Error al llograr los llugares cercanos. diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 1f1d69ea3..53bd88bac 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -13,6 +13,7 @@ Bıngeh Lokasyon Commons + Eyari Namey karberi Parola @@ -84,7 +85,8 @@ Qeyd be Heq te cı Qandê yew <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-cıkewtış</a>ê neweyi rê rapor û teklifan bıaferne. - <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politikay nımıtışi</a> + <u>Politikaya nımıtışi</u> + <u>İştırakkerdoği</u> Heq te cı Peyd rışten bırış (E-posta ra) E-posta eyar nêbi @@ -92,7 +94,7 @@ Anciya bıcerrebne Bıtexelne Ron - Lisans + Lisanso hesebiyaye Attribution-ShareAlike 3.0 Attribution 3.0 CC0 @@ -127,6 +129,8 @@ E Sername + Şınasnayış + Nuştekar Lisans Koordinati Korbıze diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 81e655b95..d9580dca1 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -162,8 +162,8 @@ Nincs leírás Ismeretlen licenc Frissítés - Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. - Szükséges engedély: Külső tárhely írása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül. + Szükséges engedély: Külső tárhely írása. Az alkalmazás nem tudja használni a kamerát enélkül. Lehetséges engedély: Jelenlegi hely megszerzése, a kategóriajavaslatok lehetőségéért. OK Közeli helyek @@ -242,6 +242,7 @@ A hely nem változott. A hely nem érhető el. Közeli helyek listájának megtekintéséhez engedély szükséges + SZÓCIKK OLVASÁSA Üdvözlünk a Wikimedia Commonson, %1$s! Örülünk, hogy itt vagy. %1$s üzenetet hagyott a vitalapodon Köszönjük a szerkesztésedet! @@ -258,5 +259,12 @@ Folytatás Mégse Újra + Ezek a helyek vannak a közeledben, amikről van Wikipédia szócikk és nincs bennük kép. + A gombra koppintva bejön egy lista, ami ezeket a helyeket mutatja. + Bármelyik helyhez feltölthetsz képet a galériádból vagy készíthetsz újat a kamerával. + Nem található kép! + Képbetöltés közben hiba történt Alkalmazás megosztása + A koordináták nem lettek megadva a kép kiválasztásakor. + Hiba a közeli helyek elérésekor. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index ba5bdfe42..f1b020965 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -263,4 +263,14 @@ Halda áfram Hætta við Reyna aftur + Náði því! + Þetta eru þeir staðir í næsta nágrenni við þig sem vantar myndir til að skýra með Wikipedia-greinar + Ef ýtt er á þennan hnapp birtist listi yfir þessa staði + Þú getur sent inn mynd úr myndasafninu þínu eða myndavélinni + Engir myndir fundust! + Villa kom upp við að hlaða inn myndum. + Sent inn af: %1$s + Deila forriti + Hnit voru ekki tilgreind við val myndar + Villa við að sækja nálæga staði. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ce71af94b..0d5a0ee3e 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -46,11 +46,7 @@ Завершение загрузки %1$s Загрузка %1$s не удалась Нажмите для просмотра - - %1$d файл загружается - %1$d файла загружается - %1$d файлов загружается - + %1$d {{PLURAL:%1$d|one=файл загружается|few=файла загружается|файлов загружается}} Мои недавние загрузки В очереди Ошибка загрузки. @@ -115,7 +111,7 @@ Почтовый клиент не установлен Недавно использованные категории Ожидание первой синхронизации… - Вы ещё не загрузили ни одной фотографии. + Вы ещё не загрузили ни одного изображения. Повторить Отмена Это изображение будет лицензировано под %1$s @@ -123,7 +119,7 @@ Скачать Лицензия по умолчанию Использовать предыдущие название/описание - Автоматически получить текущее местоположение + Анализ местоположения Получить текущее местоположение, чтобы были предложены категории, если изображение не содержит геотегов Ночной режим Использовать тёмную тему @@ -148,7 +144,7 @@ CC BY 4.0 CC Zero Викисклад содержит бо́льшую часть изображений, которые используются в Википедии. - Ваши изображения помогают образованию людей во всём мире! + Ваши изображения могут помочь образованию людей во всём мире! Пожалуйста, загрузите фотографии, которые были сняты или созданы исключительно вами: Природные объекты (например, цветы, животные, горы)\n• Полезные предметы (например, велосипеды, вокзалы)\n• Известные люди (например, ваш мэр, спортсмены-олимпийцы, которых вы встретили) Природные объекты (например, цветы, животные, горы) @@ -166,7 +162,7 @@ Категории: Sydney Opera House from the west, Sydney Opera House remote views Загрузите свои изображения. Помогите Википедии оживить статьи! Изображения в Википедии хранятся на Викискладе. - Ваши изображения помогают образованию людей во всём мире. + Ваши изображения могут помочь образованию людей во всём мире. Избегайте материалов, защищённых авторским правом, например, найденных в Интернете, изображений плакатов, книжных обложек и т.п. Вам это понятно? Да! @@ -211,7 +207,7 @@ Facebook-страница Commons Исходные коды Commons на гитхабе Фоновое изображение - Ошибка медиаизображения + Ошибка медиафайла Изображение не найдено Загрузить изображение Гора Зао @@ -246,7 +242,7 @@ Пожалуйста, подробно опишите загружаемый файл: где он был снят? что на нём изображено? каков его контекст? Пожалуйста опишите изображённых персон или объекты. Добавьте информацию, о которой нельзя легко догадаться, например, время суток, когда снимался файл. Если снято что-то необычное, постарайтесь пояснить, что именно в этом необычного. Это изображение слишком тёмное. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. Это изображение размыто. Вы уверены, что хотите его загрузить? Викисклад подходит только для фотографий, имеющих энциклопедическую ценность. - Дать разрешение + Разрешить Использовать внешнее хранилище Сохранять изображения, сделанные с помощью встроенной камеры на устройстве Войдите в свою учётную запись @@ -294,4 +290,5 @@ Загружено участником %1$s Поделиться приложением Во время выбора изображения не были указаны координаты + Ошибка получения мест поблизости diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 17a324a97..1d2fc868e 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -280,4 +280,5 @@ Uppladdad av: %1$s Dela app Koordinater specificerades inte vid bildvalet + Fel uppstod när platser i närheten hämtades. diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index dadd40917..c9dd60b41 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -280,4 +280,5 @@ Yükleyen: %1$s Uygulamayı Paylaş Koordinatlar görüntü seçimi sırasında belirlenmedi + Yakındaki yerler alınırken hata oluştu. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index a4ed20320..1aba51e02 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -279,4 +279,11 @@ Виконується Скасувати Повторити + Зрозуміло + Натискання цієї кнопки згенерує список таких місць + Зображень не знайдено! + Сталася помилка при завантаженні зображень. + Завантажено: %1$s + Поділитися програмою + Помилка отримання місць поблизу. From ce2be2401876182618191e6201f15c884de6e0be Mon Sep 17 00:00:00 2001 From: neslihanturan Date: Thu, 24 May 2018 14:59:08 +0300 Subject: [PATCH 30/94] Update PR Template to describe title format --- PULL_REQUEST_TEMPLATE.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 9d7150008..dffa9d28d 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,7 @@ +## Title (required) + +Fixes #{GitHub issue number and title (Please do not forget adding title) } + ## Description (required) Fixes #{GitHub issue number and title} From 41acb76bd8b8613eed458484b71ceaf2158d98a4 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Thu, 24 May 2018 18:04:04 +0530 Subject: [PATCH 31/94] Show nominate for deletion only for own uploads (#1541) --- .../commons/media/MediaDetailFragment.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java index 9614c4f00..fcfb1f4d9 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java @@ -23,9 +23,6 @@ import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -35,6 +32,9 @@ import java.util.Locale; import javax.inject.Inject; import javax.inject.Provider; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; import fr.free.nrw.commons.License; import fr.free.nrw.commons.LicenseList; import fr.free.nrw.commons.Media; @@ -56,16 +56,16 @@ import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailFragment extends CommonsDaggerSupportFragment { private boolean editable; - private boolean isFeaturedMedia; + private boolean isCategoryImage; private MediaDetailPagerFragment.MediaDetailProvider detailProvider; private int index; - public static MediaDetailFragment forMedia(int index, boolean editable, boolean isFeaturedMedia) { + public static MediaDetailFragment forMedia(int index, boolean editable, boolean isCategoryImage) { MediaDetailFragment mf = new MediaDetailFragment(); Bundle state = new Bundle(); state.putBoolean("editable", editable); - state.putBoolean("isFeaturedMedia", isFeaturedMedia); + state.putBoolean("isCategoryImage", isCategoryImage); state.putInt("index", index); state.putInt("listIndex", 0); state.putInt("listTop", 0); @@ -128,7 +128,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { super.onSaveInstanceState(outState); outState.putInt("index", index); outState.putBoolean("editable", editable); - outState.putBoolean("isFeaturedMedia", isFeaturedMedia); + outState.putBoolean("isCategoryImage", isCategoryImage); getScrollPosition(); outState.putInt("listTop", initialListTop); @@ -144,12 +144,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (savedInstanceState != null) { editable = savedInstanceState.getBoolean("editable"); - isFeaturedMedia = savedInstanceState.getBoolean("isFeaturedMedia"); + isCategoryImage = savedInstanceState.getBoolean("isCategoryImage"); index = savedInstanceState.getInt("index"); initialListTop = savedInstanceState.getInt("listTop"); } else { editable = getArguments().getBoolean("editable"); - isFeaturedMedia = getArguments().getBoolean("isFeaturedMedia"); + isCategoryImage = getArguments().getBoolean("isCategoryImage"); index = getArguments().getInt("index"); initialListTop = 0; } @@ -161,7 +161,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { ButterKnife.bind(this,view); - if (isFeaturedMedia){ + if (isCategoryImage){ authorLayout.setVisibility(VISIBLE); } else { authorLayout.setVisibility(GONE); @@ -328,7 +328,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (!TextUtils.isEmpty(licenseLink(media))) { openWebBrowser(licenseLink(media)); } else { - if(isFeaturedMedia) { + if(isCategoryImage) { Timber.d("Unable to fetch license URL for %s", media.getLicense()); } else { Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT); @@ -503,8 +503,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { if (media.getRequestedDeletion()){ delete.setVisibility(GONE); nominatedForDeletion.setVisibility(VISIBLE); - } - else{ + } else if (!isCategoryImage) { delete.setVisibility(VISIBLE); nominatedForDeletion.setVisibility(GONE); } From 2a0b9d8a0bac0116fc442f4b43fcf0a09df5ae85 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Thu, 24 May 2018 18:24:31 +0530 Subject: [PATCH 32/94] =?UTF-8?q?Enable=20crosswiki=20notifications=20and?= =?UTF-8?q?=20minor=20UI=20fixes=20in=20displaying=20notif=E2=80=A6=20(#15?= =?UTF-8?q?40)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable crosswiki notifications and minor UI fixes in displaying notifications * Added java docs --- .travis.yml | 3 +- app/build.gradle | 6 ++- app/proguard-glide.txt | 9 ++++ .../fr/free/nrw/commons/glide/SvgDecoder.java | 36 +++++++++++++ .../commons/glide/SvgDrawableTranscoder.java | 28 ++++++++++ .../commons/glide/SvgSoftwareLayerSetter.java | 51 ++++++++++++++++++ .../mwapi/ApacheHttpClientMediaWikiApi.java | 2 +- .../notification/NotificationRenderer.java | 38 +++++++++---- .../notification/NotificationUtils.java | 54 +++++++++++++++++-- .../nrw/commons/notification/SvgModule.java | 35 ++++++++++++ gradle.properties | 8 +-- 11 files changed, 247 insertions(+), 23 deletions(-) create mode 100644 app/proguard-glide.txt create mode 100644 app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java create mode 100644 app/src/main/java/fr/free/nrw/commons/glide/SvgDrawableTranscoder.java create mode 100644 app/src/main/java/fr/free/nrw/commons/glide/SvgSoftwareLayerSetter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/notification/SvgModule.java diff --git a/.travis.yml b/.travis.yml index 20c5bfaee..5e76e09d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,12 +19,13 @@ android: components: - tools - platform-tools - - build-tools-26.0.2 + - build-tools-27.0.0 - extra-google-m2repository - extra-android-m2repository - ${ANDROID_TARGET} - android-25 - android-26 + - android-27 - sys-img-${ANDROID_ABI}-${ANDROID_TARGET} licenses: - 'android-sdk-license-.+' diff --git a/app/build.gradle b/app/build.gradle index 5d37f8f54..d2b9463c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,6 +69,10 @@ dependencies { testImplementation 'com.nhaarman:mockito-kotlin:1.5.0' testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' + implementation 'com.caverock:androidsvg:1.2.1' + implementation 'com.github.bumptech.glide:glide:4.7.1' + kapt 'com.github.bumptech.glide:compiler:4.7.1' + androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION" @@ -117,7 +121,7 @@ android { buildTypes { release { minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient. - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt' } debug { applicationIdSuffix ".debug" diff --git a/app/proguard-glide.txt b/app/proguard-glide.txt new file mode 100644 index 000000000..ef3437660 --- /dev/null +++ b/app/proguard-glide.txt @@ -0,0 +1,9 @@ +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + +# for DexGuard only +-keepresourcexmlelements manifest/application/meta-data@value=GlideModule \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java b/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java new file mode 100644 index 000000000..9087f9501 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java @@ -0,0 +1,36 @@ +package fr.free.nrw.commons.glide; + +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.ResourceDecoder; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.SimpleResource; +import com.caverock.androidsvg.SVG; +import com.caverock.androidsvg.SVGParseException; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Decodes an SVG internal representation from an {@link InputStream}. + */ +public class SvgDecoder implements ResourceDecoder { + + @Override + public boolean handles(@NonNull InputStream source, @NonNull Options options) { + // TODO: Can we tell? + return true; + } + + public Resource decode(@NonNull InputStream source, int width, int height, + @NonNull Options options) + throws IOException { + try { + SVG svg = SVG.getFromInputStream(source); + return new SimpleResource<>(svg); + } catch (SVGParseException ex) { + throw new IOException("Cannot load SVG from stream", ex); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/glide/SvgDrawableTranscoder.java b/app/src/main/java/fr/free/nrw/commons/glide/SvgDrawableTranscoder.java new file mode 100644 index 000000000..89910c8fb --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/glide/SvgDrawableTranscoder.java @@ -0,0 +1,28 @@ +package fr.free.nrw.commons.glide; + +import android.graphics.Picture; +import android.graphics.drawable.PictureDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.bumptech.glide.load.Options; +import com.bumptech.glide.load.engine.Resource; +import com.bumptech.glide.load.resource.SimpleResource; +import com.bumptech.glide.load.resource.transcode.ResourceTranscoder; +import com.caverock.androidsvg.SVG; + +/** + * Convert the {@link SVG}'s internal representation to an Android-compatible one + * ({@link Picture}). + */ +public class SvgDrawableTranscoder implements ResourceTranscoder { + @Nullable + @Override + public Resource transcode(@NonNull Resource toTranscode, + @NonNull Options options) { + SVG svg = toTranscode.get(); + Picture picture = svg.renderToPicture(); + PictureDrawable drawable = new PictureDrawable(picture); + return new SimpleResource<>(drawable); + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/glide/SvgSoftwareLayerSetter.java b/app/src/main/java/fr/free/nrw/commons/glide/SvgSoftwareLayerSetter.java new file mode 100644 index 000000000..66a3bd6bf --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/glide/SvgSoftwareLayerSetter.java @@ -0,0 +1,51 @@ +package fr.free.nrw.commons.glide; + +import android.graphics.drawable.PictureDrawable; +import android.widget.ImageView; + +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.target.ImageViewTarget; +import com.bumptech.glide.request.target.Target; + +/** + * Listener which updates the {@link ImageView} to be software rendered, because + * {@link com.caverock.androidsvg.SVG SVG}/{@link android.graphics.Picture Picture} can't render on + * a hardware backed {@link android.graphics.Canvas Canvas}. + */ +public class SvgSoftwareLayerSetter implements RequestListener { + + /** + * Sets the layer type to none if the load fails + * @param e + * @param model + * @param target + * @param isFirstResource + * @return + */ + @Override + public boolean onLoadFailed(GlideException e, Object model, Target target, + boolean isFirstResource) { + ImageView view = ((ImageViewTarget) target).getView(); + view.setLayerType(ImageView.LAYER_TYPE_NONE, null); + return false; + } + + /** + * Sets the layer type to software when the resource is ready + * @param resource + * @param model + * @param target + * @param dataSource + * @param isFirstResource + * @return + */ + @Override + public boolean onResourceReady(PictureDrawable resource, Object model, + Target target, DataSource dataSource, boolean isFirstResource) { + ImageView view = ((ImageViewTarget) target).getView(); + view.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null); + return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 6629d0933..e962bdaf3 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -444,8 +444,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .param("notprop", "list") .param("format", "xml") .param("meta", "notifications") -// .param("meta", "notifications") .param("notformat", "model") + .param("notwikis", "wikidatawiki|commonswiki|enwiki") .get() .getNode("/api/query/notifications/list"); } catch (IOException e) { diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java index 17a318e74..6dcfca35d 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.notification; -import android.util.Log; +import android.graphics.drawable.PictureDrawable; +import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -8,17 +9,23 @@ import android.widget.ImageView; import android.widget.TextView; import com.borjabravo.readmoretextview.ReadMoreTextView; +import com.bumptech.glide.RequestBuilder; import com.pedrogomez.renderers.Renderer; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.glide.SvgSoftwareLayerSetter; + +import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; /** * Created by root on 19.12.2017. */ public class NotificationRenderer extends Renderer { + private RequestBuilder requestBuilder; + @BindView(R.id.title) ReadMoreTextView title; @BindView(R.id.time) TextView time; @BindView(R.id.icon) ImageView icon; @@ -41,23 +48,32 @@ public class NotificationRenderer extends Renderer { protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) { View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false); ButterKnife.bind(this, inflatedView); + requestBuilder = GlideApp.with(inflatedView.getContext()) + .as(PictureDrawable.class) + .error(R.drawable.round_icon_unknown) + .transition(withCrossFade()) + .listener(new SvgSoftwareLayerSetter()); return inflatedView; } @Override public void render() { Notification notification = getContent(); - String str = notification.notificationText.trim(); - str = str.concat(" "); - title.setText(str); + setTitle(notification.notificationText); time.setText(notification.date); - switch (notification.notificationType) { - case THANK_YOU_EDIT: - icon.setImageResource(R.drawable.ic_edit_black_24dp); - break; - default: - icon.setImageResource(R.drawable.round_icon_unknown); - } + requestBuilder.load(notification.iconUrl).into(icon); + } + + /** + * Cleans up the notification text and sets it as the title + * Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification + * @param notificationText + */ + private void setTitle(String notificationText) { + notificationText = notificationText.trim().replaceAll("(^\\h*)|(\\h*$)", ""); + notificationText = Html.fromHtml(notificationText).toString(); + notificationText = notificationText.concat(" "); + title.setText(notificationText); } public interface NotificationClicked{ diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java index 68c3add1c..e7c87d3f4 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationUtils.java @@ -16,12 +16,13 @@ import javax.annotation.Nullable; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.R; -import static fr.free.nrw.commons.notification.NotificationType.THANK_YOU_EDIT; import static fr.free.nrw.commons.notification.NotificationType.UNKNOWN; public class NotificationUtils { private static final String COMMONS_WIKI = "commonswiki"; + private static final String WIKIDATA_WIKI = "wikidatawiki"; + private static final String WIKIPEDIA_WIKI = "enwiki"; public static boolean isCommonsNotification(Node document) { if (document == null || !document.hasAttributes()) { @@ -31,6 +32,32 @@ public class NotificationUtils { return COMMONS_WIKI.equals(element.getAttribute("wiki")); } + /** + * Returns true if the wiki attribute corresponds to wikidatawiki + * @param document + * @return + */ + public static boolean isWikidataNotification(Node document) { + if (document == null || !document.hasAttributes()) { + return false; + } + Element element = (Element) document; + return WIKIDATA_WIKI.equals(element.getAttribute("wiki")); + } + + /** + * Returns true if the wiki attribute corresponds to enwiki + * @param document + * @return + */ + public static boolean isWikipediaNotification(Node document) { + if (document == null || !document.hasAttributes()) { + return false; + } + Element element = (Element) document; + return WIKIPEDIA_WIKI.equals(element.getAttribute("wiki")); + } + public static NotificationType getNotificationType(Node document) { Element element = (Element) document; String type = element.getAttribute("type"); @@ -68,10 +95,17 @@ public class NotificationUtils { return notifications; } + /** + * Currently the app is interested in showing notifications just from the following three wikis: commons, wikidata, wikipedia + * This function returns true only if the notification belongs to any of the above wikis and is of a known notification type + * @param node + * @return + */ private static boolean isUsefulNotification(Node node) { - return isCommonsNotification(node) - && !getNotificationType(node).equals(UNKNOWN) - && !getNotificationType(node).equals(THANK_YOU_EDIT); + return (isCommonsNotification(node) + || isWikidataNotification(node) + || isWikipediaNotification(node)) + && !getNotificationType(node).equals(UNKNOWN); } public static boolean isBundledNotification(Node document) { @@ -97,7 +131,7 @@ public class NotificationUtils { switch (type) { case THANK_YOU_EDIT: - notificationText = context.getString(R.string.notifications_thank_you_edit); + notificationText = getThankYouEditDescription(document); break; case EDIT_USER_TALK: notificationText = getNotificationText(document); @@ -146,6 +180,16 @@ public class NotificationUtils { return body != null ? body.getTextContent() : ""; } + /** + * Gets the header node returned in the XML document to form the description for thank you edits + * @param document + * @return + */ + private static String getThankYouEditDescription(Node document) { + Node body = getNode(getModel(document), "header"); + return body != null ? body.getTextContent() : ""; + } + private static String getNotificationIconUrl(Node document) { String format = "%s%s"; Node iconUrl = getNode(getModel(document), "iconUrl"); diff --git a/app/src/main/java/fr/free/nrw/commons/notification/SvgModule.java b/app/src/main/java/fr/free/nrw/commons/notification/SvgModule.java new file mode 100644 index 000000000..5a1e8ae63 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/notification/SvgModule.java @@ -0,0 +1,35 @@ +package fr.free.nrw.commons.notification; + +import android.content.Context; +import android.graphics.drawable.PictureDrawable; +import android.support.annotation.NonNull; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.Registry; +import com.bumptech.glide.annotation.GlideModule; +import com.bumptech.glide.module.AppGlideModule; +import com.caverock.androidsvg.SVG; + +import java.io.InputStream; + +import fr.free.nrw.commons.glide.SvgDecoder; +import fr.free.nrw.commons.glide.SvgDrawableTranscoder; + +/** + * Module for the SVG sample app. + */ +@GlideModule +public class SvgModule extends AppGlideModule { + @Override + public void registerComponents(@NonNull Context context, @NonNull Glide glide, + @NonNull Registry registry) { + registry.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder()) + .append(InputStream.class, SVG.class, new SvgDecoder()); + } + + // Disable manifest parsing to avoid adding similar modules twice. + @Override + public boolean isManifestParsingEnabled() { + return false; + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 850003852..05aa34949 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,17 +14,17 @@ # org.gradle.parallel=true #Thu Mar 01 15:28:48 IST 2018 systemProp.http.proxyPort=0 -compileSdkVersion=android-26 +compileSdkVersion=android-27 android.useDeprecatedNdk=true BUTTERKNIFE_VERSION=8.6.0 org.gradle.jvmargs=-Xmx1536M -buildToolsVersion=26.0.2 -targetSdkVersion=25 +buildToolsVersion=27.0.0 +targetSdkVersion=27 #TODO: Temporary disabled. https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#aapt2 #Refer to PR: https://github.com/commons-app/apps-android-commons/pull/932 android.enableAapt2=false -SUPPORT_LIB_VERSION=26.0.2 +SUPPORT_LIB_VERSION=27.1.1 minSdkVersion=15 systemProp.http.proxyHost= LEAK_CANARY=1.5.4 From 2255bd9a566accae08c0cd79e6105e270c7e0299 Mon Sep 17 00:00:00 2001 From: ShridharGoel <35566748+ShridharGoel@users.noreply.github.com> Date: Thu, 24 May 2018 18:57:13 +0530 Subject: [PATCH 33/94] Fix for issue #1508 (#1512) * Made changes to the error message when user enters wrong login credentials. * Used a single string for the error message when user enters wrong login credentials. --- app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java | 4 ++-- app/src/main/res/values/strings.xml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java index d0fb628e3..b19d70a4e 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java @@ -271,11 +271,11 @@ public class LoginActivity extends AccountAuthenticatorActivity { showMessageAndCancelDialog(R.string.login_failed_network); } else if (result.toLowerCase(Locale.getDefault()).contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) { // Matches nosuchuser, nosuchusershort, noname - showMessageAndCancelDialog(R.string.login_failed_username); + showMessageAndCancelDialog(R.string.login_failed_wrong_credentials); emptySensitiveEditFields(); } else if (result.toLowerCase(Locale.getDefault()).contains("wrongpassword".toLowerCase())) { // Matches wrongpassword, wrongpasswordempty - showMessageAndCancelDialog(R.string.login_failed_password); + showMessageAndCancelDialog(R.string.login_failed_wrong_credentials); emptySensitiveEditFields(); } else if (result.toLowerCase(Locale.getDefault()).contains("throttle".toLowerCase())) { // Matches unknown throttle error codes diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6e30baa10..ba982e625 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,8 +46,7 @@ Please provide a title for this file Description Unable to login - network failure - Unable to login - please check your username - Unable to login - please check your password + Unable to login - please check your username and password Too many unsuccessful attempts. Please try again in a few minutes. Sorry, this user has been blocked on Commons You must provide your two factor authentication code. From 32cb8df9aea4dde855a08ba610c580566b2870a1 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sat, 9 Dec 2017 23:01:47 -0600 Subject: [PATCH 34/94] Consolidate the networking libraries - drop volley in favor of OkHttp --- app/build.gradle | 1 - app/proguard-rules.txt | 2 +- .../nrw/commons/caching/CacheController.java | 6 +- .../category/CategorizationFragment.java | 6 +- .../commons/di/CommonsApplicationModule.java | 21 +- .../free/nrw/commons/upload/CategoryApi.java | 165 ++++++++++++ .../free/nrw/commons/upload/MwVolleyApi.java | 249 ------------------ .../nrw/commons/upload/ShareActivity.java | 6 +- 8 files changed, 194 insertions(+), 262 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java diff --git a/app/build.gradle b/app/build.gradle index d2b9463c1..4350f1f7c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,7 +11,6 @@ dependencies { implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar' implementation 'in.yuvi:http.fluent:1.3' implementation 'com.github.chrisbanes:PhotoView:2.0.0' - implementation 'com.android.volley:volley:1.0.0' implementation 'ch.acra:acra:4.9.2' implementation 'org.mediawiki:api:1.3' implementation 'commons-codec:commons-codec:1.10' diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index bbf3a3f0d..f3e00b3f5 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -1,5 +1,5 @@ -dontobfuscate -keep class org.apache.http.** { *; } -dontwarn org.apache.http.** --keep class fr.free.nrw.commons.upload.MwVolleyApi$Page {*;} +-keep class fr.free.nrw.commons.upload.CategoryApi$Page {*;} -keep class android.support.v7.widget.ShareActionProvider { *; } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java index ff6ceece4..1b48c3c50 100644 --- a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java +++ b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import fr.free.nrw.commons.upload.MwVolleyApi; +import fr.free.nrw.commons.upload.CategoryApi; import timber.log.Timber; public class CacheController { @@ -31,8 +31,8 @@ public class CacheController { public void cacheCategory() { List pointCatList = new ArrayList<>(); - if (MwVolleyApi.GpsCatExists.getGpsCatExists()) { - pointCatList.addAll(MwVolleyApi.getGpsCat()); + if (CategoryApi.GpsCatExists.getGpsCatExists()) { + pointCatList.addAll(CategoryApi.getGpsCat()); Timber.d("Categories being cached: %s", pointCatList); } else { Timber.d("No categories found, so no categories cached"); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java index e804189ab..514b2fb5f 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java @@ -39,7 +39,7 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.upload.MwVolleyApi; +import fr.free.nrw.commons.upload.CategoryApi; import fr.free.nrw.commons.utils.StringSortingUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -288,8 +288,8 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment { private Observable gpsCategories() { return Observable.fromIterable( - MwVolleyApi.GpsCatExists.getGpsCatExists() - ? MwVolleyApi.getGpsCat() : new ArrayList<>()) + CategoryApi.GpsCatExists.getGpsCatExists() + ? CategoryApi.getGpsCat() : new ArrayList<>()) .map(name -> new CategoryItem(name, false)); } 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 55281be7e..3d5516935 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 @@ -4,9 +4,11 @@ import android.content.ContentProviderClient; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.support.annotation.NonNull; import android.support.v4.util.LruCache; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import javax.inject.Named; import javax.inject.Singleton; @@ -14,7 +16,6 @@ import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.BuildConfig; -import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.caching.CacheController; @@ -24,6 +25,8 @@ import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; @@ -69,6 +72,12 @@ public class CommonsApplicationModule { return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY); } + @Provides + @Singleton + public OkHttpClient provideOkHttpClient() { + return new OkHttpClient.Builder().build(); + } + @Provides @Named("application_preferences") public SharedPreferences providesApplicationSharedPreferences(Context context) { @@ -126,6 +135,14 @@ public class CommonsApplicationModule { return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); } + @Provides + @Named("commons_mediawiki_url") + @NonNull + @SuppressWarnings("ConstantConditions") + public HttpUrl provideMwUrl() { + return HttpUrl.parse("https://commons.wikimedia.org/"); + } + @Provides @Singleton public LocationServiceManager provideLocationServiceManager(Context context) { @@ -139,7 +156,7 @@ public class CommonsApplicationModule { @Provides @Singleton public Gson provideGson() { - return new Gson(); + return new GsonBuilder().create(); } @Provides diff --git a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java new file mode 100644 index 000000000..8a7dfcc6e --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java @@ -0,0 +1,165 @@ +package fr.free.nrw.commons.upload; + +import android.support.annotation.NonNull; + +import com.google.gson.Gson; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.ResponseBody; +import timber.log.Timber; + +/** + * Uses the OkHttp library to implement asynchronous calls to the Commons MediaWiki API to match + * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list + * of relevant categories. + */ +public class CategoryApi { + + private static Set categorySet; + private static List categoryList; + private final OkHttpClient okHttpClient; + private final HttpUrl mwUrl; + private final Gson gson; + + @Inject + public CategoryApi(OkHttpClient okHttpClient, @Named("commons_mediawiki_url") HttpUrl mwUrl, Gson gson) { + this.okHttpClient = okHttpClient; + this.mwUrl = mwUrl; + this.gson = gson; + categorySet = new HashSet<>(); + } + + public static List getGpsCat() { + return categoryList; + } + + public static void setGpsCat(List cachedList) { + categoryList = new ArrayList<>(); + categoryList.addAll(cachedList); + Timber.d("Setting GPS cats from cache: %s", categoryList); + } + + public void request(String coords) { + String apiUrl = buildUrl(coords); + Timber.d("URL: %s", apiUrl); + + Call call = okHttpClient.newCall(new Request.Builder().get().url(apiUrl).build()); + call.enqueue(new Callback() { + @Override + public void onFailure(@NonNull Call call, @NonNull IOException e) { + Timber.e(e); + GpsCatExists.setGpsCatExists(false); + } + + @Override + public void onResponse(@NonNull Call call, @NonNull okhttp3.Response response) { + categoryList = new ArrayList<>(); + categorySet = new HashSet<>(); + ResponseBody body = response.body(); + if (body == null) { + return; + } + QueryResponse queryResponse = gson.fromJson(body.charStream(), QueryResponse.class); + if (queryResponse != null && queryResponse.query != null && queryResponse.query.pages != null) { + for (Page page : queryResponse.query.pages) { + if (page.categories != null) { + for (Category category : page.categories) { + String categoryString = category.title.replace("Category:", ""); + categorySet.add(categoryString); + } + categoryList = new ArrayList<>(categorySet); + } + } + } + GpsCatExists.setGpsCatExists(!categorySet.isEmpty()); + } + }); + } + + /** + * Builds URL with image coords for MediaWiki API calls + * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 + * + * @param coords Coordinates to build query with + * @return URL for API query + */ + private String buildUrl(String coords) { + return mwUrl.newBuilder() + .addPathSegment("w") + .addPathSegment("api.php") + .addQueryParameter("action", "query") + .addQueryParameter("prop", "categories|coordinates|pageprops") + .addQueryParameter("format", "json") + .addQueryParameter("clshow", "!hidden") + .addQueryParameter("coprop", "type|name|dim|country|region|globe") + .addQueryParameter("codistancefrompoint", coords) + .addQueryParameter("generator", "geosearch") + .addQueryParameter("ggscoord", coords) + .addQueryParameter("ggsradius", "10000") + .addQueryParameter("ggslimit", "10") + .addQueryParameter("ggsnamespace", "6") + .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") + .addQueryParameter("ggsprimary", "all") + .addQueryParameter("formatversion", "2") + .build().toString(); + } + + public static class GpsCatExists { + private static boolean gpsCatExists; + + public static void setGpsCatExists(boolean gpsCat) { + gpsCatExists = gpsCat; + } + + public static boolean getGpsCatExists() { + return gpsCatExists; + } + } + + private static class QueryResponse { + public Query query; + + public QueryResponse() { + } + } + + private static class Query { + public Page[] pages; + + public Query() { + pages = new Page[0]; + } + } + + private static class Page { + public String title; + public Category[] categories; + public Category category; + + public Page() { + } + } + + private static class Category { + public String title; + + public Category() { + } + } +} + + + diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java b/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java deleted file mode 100644 index a530e79e6..000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/MwVolleyApi.java +++ /dev/null @@ -1,249 +0,0 @@ -package fr.free.nrw.commons.upload; - -import android.content.Context; -import android.net.Uri; - -import com.android.volley.Cache; -import com.android.volley.NetworkResponse; -import com.android.volley.Request; -import com.android.volley.RequestQueue; -import com.android.volley.Response; -import com.android.volley.VolleyError; -import com.android.volley.toolbox.HttpHeaderParser; -import com.android.volley.toolbox.JsonRequest; -import com.android.volley.toolbox.Volley; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.UnsupportedEncodingException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import timber.log.Timber; - -/** - * Uses the Volley library to implement asynchronous calls to the Commons MediaWiki API to match - * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list - * of relevant categories. - */ -public class MwVolleyApi { - - private static RequestQueue REQUEST_QUEUE; - private static final Gson GSON = new GsonBuilder().create(); - - private static Set categorySet; - private static List categoryList; - - private static final String MWURL = "https://commons.wikimedia.org/"; - private final Context context; - - public MwVolleyApi(Context context) { - this.context = context; - categorySet = new HashSet<>(); - } - - public static List getGpsCat() { - return categoryList; - } - - public static void setGpsCat(List cachedList) { - categoryList = new ArrayList<>(); - categoryList.addAll(cachedList); - Timber.d("Setting GPS cats from cache: %s", categoryList); - } - - public void request(String coords) { - String apiUrl = buildUrl(coords); - Timber.d("URL: %s", apiUrl); - - JsonRequest request = new QueryRequest(apiUrl, - new LogResponseListener<>(), new LogResponseErrorListener()); - getQueue().add(request); - } - - /** - * Builds URL with image coords for MediaWiki API calls - * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 - * @param coords Coordinates to build query with - * @return URL for API query - */ - private String buildUrl(String coords) { - - Uri.Builder builder = Uri.parse(MWURL).buildUpon(); - - builder.appendPath("w") - .appendPath("api.php") - .appendQueryParameter("action", "query") - .appendQueryParameter("prop", "categories|coordinates|pageprops") - .appendQueryParameter("format", "json") - .appendQueryParameter("clshow", "!hidden") - .appendQueryParameter("coprop", "type|name|dim|country|region|globe") - .appendQueryParameter("codistancefrompoint", coords) - .appendQueryParameter("generator", "geosearch") - .appendQueryParameter("ggscoord", coords) - .appendQueryParameter("ggsradius", "10000") - .appendQueryParameter("ggslimit", "10") - .appendQueryParameter("ggsnamespace", "6") - .appendQueryParameter("ggsprop", "type|name|dim|country|region|globe") - .appendQueryParameter("ggsprimary", "all") - .appendQueryParameter("formatversion", "2"); - - return builder.toString(); - } - - private synchronized RequestQueue getQueue() { - if (REQUEST_QUEUE == null) { - REQUEST_QUEUE = Volley.newRequestQueue(context); - } - return REQUEST_QUEUE; - } - - private static class LogResponseListener implements Response.Listener { - - @Override - public void onResponse(T response) { - Timber.d(response.toString()); - } - } - - private static class LogResponseErrorListener implements Response.ErrorListener { - - @Override - public void onErrorResponse(VolleyError error) { - Timber.e(error.toString()); - } - } - - private static class QueryRequest extends JsonRequest { - - public QueryRequest(String url, - Response.Listener listener, - Response.ErrorListener errorListener) { - super(Request.Method.GET, url, null, listener, errorListener); - } - - @Override - protected Response parseNetworkResponse(NetworkResponse response) { - String json = parseString(response); - QueryResponse queryResponse = GSON.fromJson(json, QueryResponse.class); - return Response.success(queryResponse, cacheEntry(response)); - } - - private Cache.Entry cacheEntry(NetworkResponse response) { - return HttpHeaderParser.parseCacheHeaders(response); - } - - private String parseString(NetworkResponse response) { - try { - return new String(response.data, HttpHeaderParser.parseCharset(response.headers)); - } catch (UnsupportedEncodingException e) { - return new String(response.data); - } - } - } - - public static class GpsCatExists { - private static boolean gpsCatExists; - - public static void setGpsCatExists(boolean gpsCat) { - gpsCatExists = gpsCat; - } - - public static boolean getGpsCatExists() { - return gpsCatExists; - } - } - - private static class QueryResponse { - private Query query = new Query(); - - private String printSet() { - if (categorySet == null || categorySet.isEmpty()) { - GpsCatExists.setGpsCatExists(false); - Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists()); - return "No collection of categories"; - } else { - GpsCatExists.setGpsCatExists(true); - Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists()); - return "CATEGORIES FOUND" + categorySet.toString(); - } - - } - - @Override - public String toString() { - if (query != null) { - return "query=" + query.toString() + "\n" + printSet(); - } else { - return "No pages found"; - } - } - } - - private static class Query { - private Page [] pages; - - @Override - public String toString() { - StringBuilder builder = new StringBuilder("pages=" + "\n"); - if (pages != null) { - for (Page page : pages) { - builder.append(page.toString()); - builder.append("\n"); - } - builder.replace(builder.length() - 1, builder.length(), ""); - return builder.toString(); - } else { - return "No pages found"; - } - } - } - - public static class Page { - private int pageid; - private int ns; - private String title; - private Category[] categories; - private Category category; - - public Page() { - } - - @Override - public String toString() { - - StringBuilder builder = new StringBuilder("PAGEID=" + pageid + " ns=" + ns + " title=" + title + "\n" + " CATEGORIES= "); - - if (categories == null || categories.length == 0) { - builder.append("no categories exist\n"); - } else { - for (Category category : categories) { - builder.append(category.toString()); - builder.append("\n"); - if (category != null) { - String categoryString = category.toString().replace("Category:", ""); - categorySet.add(categoryString); - } - } - } - - categoryList = new ArrayList<>(categorySet); - builder.replace(builder.length() - 1, builder.length(), ""); - return builder.toString(); - } - } - - private static class Category { - private String title; - - @Override - public String toString() { - return title; - } - } -} - - - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index cfcec1da5..e3188534c 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -123,6 +123,8 @@ public class ShareActivity @Inject ModifierSequenceDao modifierSequenceDao; @Inject + CategoryApi apiCall; + @Inject @Named("default_preferences") SharedPreferences prefs; @@ -699,8 +701,6 @@ public class ShareActivity cacheController.setQtPoint(decLongitude, decLatitude); } - MwVolleyApi apiCall = new MwVolleyApi(this); - List displayCatList = cacheController.findCategory(); boolean catListEmpty = displayCatList.isEmpty(); @@ -712,7 +712,7 @@ public class ShareActivity } else { cacheFound = true; Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); - MwVolleyApi.setGpsCat(displayCatList); + CategoryApi.setGpsCat(displayCatList); } }else{ Timber.d("EXIF: no coords"); From c7948c817b03b8a38c8dfd12d65a7e6908eff2cd Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 8 Apr 2018 15:55:58 -0500 Subject: [PATCH 35/94] Extracted a few networking related items into a new Dagger module and finished the process of mocking the main component for tests. --- .../di/CommonsApplicationComponent.java | 5 +- .../commons/di/CommonsApplicationModule.java | 42 ------------- .../free/nrw/commons/di/NetworkingModule.java | 59 +++++++++++++++++++ .../nrw/commons/TestCommonsApplication.kt | 20 +++++-- 4 files changed, 75 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java index 91f6d4ccb..5662bb885 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java @@ -9,17 +9,16 @@ import dagger.android.support.AndroidSupportInjectionModule; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.MediaWikiImageView; import fr.free.nrw.commons.auth.LoginActivity; -import fr.free.nrw.commons.contributions.Contribution; -import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.contributions.ContributionsSyncAdapter; import fr.free.nrw.commons.delete.DeleteTask; import fr.free.nrw.commons.modifications.ModificationsSyncAdapter; -import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.nearby.PlaceRenderer; +import fr.free.nrw.commons.settings.SettingsFragment; @Singleton @Component(modules = { CommonsApplicationModule.class, + NetworkingModule.class, AndroidInjectionModule.class, AndroidSupportInjectionModule.class, ActivityBuilderModule.class, 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 3d5516935..b0aa3e5e6 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 @@ -4,29 +4,21 @@ import android.content.ContentProviderClient; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; import android.support.v4.util.LruCache; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; -import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.location.LocationServiceManager; -import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; @@ -36,7 +28,6 @@ import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MOD @SuppressWarnings({"WeakerAccess", "unused"}) public class CommonsApplicationModule { public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider"; - public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; private Context applicationContext; @@ -72,12 +63,6 @@ public class CommonsApplicationModule { return context.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY); } - @Provides - @Singleton - public OkHttpClient provideOkHttpClient() { - return new OkHttpClient.Builder().build(); - } - @Provides @Named("application_preferences") public SharedPreferences providesApplicationSharedPreferences(Context context) { @@ -126,39 +111,12 @@ public class CommonsApplicationModule { return new SessionManager(context, mediaWikiApi, sharedPreferences); } - @Provides - @Singleton - public MediaWikiApi provideMediaWikiApi(Context context, - @Named("default_preferences") SharedPreferences defaultPreferences, - @Named("category_prefs") SharedPreferences categoryPrefs, - Gson gson) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); - } - - @Provides - @Named("commons_mediawiki_url") - @NonNull - @SuppressWarnings("ConstantConditions") - public HttpUrl provideMwUrl() { - return HttpUrl.parse("https://commons.wikimedia.org/"); - } - @Provides @Singleton public LocationServiceManager provideLocationServiceManager(Context context) { return new LocationServiceManager(context); } - /** - * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. - * @return returns a singleton Gson instance - */ - @Provides - @Singleton - public Gson provideGson() { - return new GsonBuilder().create(); - } - @Provides @Singleton public CacheController provideCacheController() { 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 new file mode 100644 index 000000000..8c0b52316 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -0,0 +1,59 @@ +package fr.free.nrw.commons.di; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.NonNull; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import javax.inject.Named; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; +import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; + +@Module +@SuppressWarnings({"WeakerAccess", "unused"}) +public class NetworkingModule { + public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024; + + @Provides + @Singleton + public OkHttpClient provideOkHttpClient() { + return new OkHttpClient.Builder().build(); + } + + @Provides + @Singleton + public MediaWikiApi provideMediaWikiApi(Context context, + @Named("default_preferences") SharedPreferences defaultPreferences, + @Named("category_prefs") SharedPreferences categoryPrefs, + Gson gson) { + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); + } + + @Provides + @Named("commons_mediawiki_url") + @NonNull + @SuppressWarnings("ConstantConditions") + public HttpUrl provideMwUrl() { + return HttpUrl.parse(BuildConfig.COMMONS_URL); + } + + /** + * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. + * @return returns a singleton Gson instance + */ + @Provides + @Singleton + public Gson provideGson() { + return new GsonBuilder().create(); + } + +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index b1de29143..076b3e745 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons +import android.content.ContentProviderClient import android.content.Context import android.content.SharedPreferences import android.support.v4.util.LruCache -import com.google.gson.Gson import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil @@ -33,21 +33,31 @@ class TestCommonsApplication : CommonsApplication() { override fun setupLeakCanary(): RefWatcher = RefWatcher.DISABLED } +@Suppress("MemberVisibilityCanBePrivate") class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) { val accountUtil: AccountUtil = mock() val appSharedPreferences: SharedPreferences = mock() val defaultSharedPreferences: SharedPreferences = mock() - val categorySharedPreferences: SharedPreferences = mock() val otherSharedPreferences: SharedPreferences = mock() val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() - val mediaWikiApi: MediaWikiApi = mock() val locationServiceManager: LocationServiceManager = mock() val cacheController: CacheController = mock() val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() - val gson: Gson = Gson() + val categoryClient: ContentProviderClient = mock() + val contributionClient: ContentProviderClient = mock() + val modificationClient: ContentProviderClient = mock() + val uploadPrefs: SharedPreferences = mock() + + override fun provideCategoryContentProviderClient(context: Context?): ContentProviderClient = categoryClient + + override fun provideContributionContentProviderClient(context: Context?): ContentProviderClient = contributionClient + + override fun provideModificationContentProviderClient(context: Context?): ContentProviderClient = modificationClient + + override fun providesDirectNearbyUploadPreferences(context: Context?): SharedPreferences = uploadPrefs override fun providesAccountUtil(context: Context): AccountUtil = accountUtil @@ -61,8 +71,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager - override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences, categorySharedPreferences: SharedPreferences, gson: Gson): MediaWikiApi = mediaWikiApi - override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager override fun provideCacheController(): CacheController = cacheController From a66a0e8ca07cbe942bf02e562c610ace8aecfca2 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Sun, 15 Apr 2018 07:32:14 -0500 Subject: [PATCH 36/94] Refactoring to extract GpsCategoryModel and ensure single-responsibility-principle is maintained in CategoryApi. --- app/proguard-rules.txt | 1 - .../nrw/commons/caching/CacheController.java | 19 +- .../category/CategorizationFragment.java | 8 +- .../commons/di/CommonsApplicationModule.java | 7 - .../free/nrw/commons/mwapi/CategoryApi.java | 101 ++++++++++ .../nrw/commons/mwapi/model/ApiResponse.java | 12 ++ .../fr/free/nrw/commons/mwapi/model/Page.java | 17 ++ .../nrw/commons/mwapi/model/PageCategory.java | 12 ++ .../free/nrw/commons/mwapi/model/Query.java | 10 + .../free/nrw/commons/upload/CategoryApi.java | 165 ---------------- .../nrw/commons/upload/GpsCategoryModel.java | 40 ++++ .../nrw/commons/upload/ShareActivity.java | 23 ++- .../nrw/commons/TestCommonsApplication.kt | 4 - .../free/nrw/commons/mwapi/CategoryApiTest.kt | 178 ++++++++++++++++++ .../commons/mwapi/model/ApiResponseTest.kt | 28 +++ .../commons/mwapi/model/PageCategoryTest.kt | 20 ++ .../free/nrw/commons/mwapi/model/PageTest.kt | 12 ++ .../commons/upload/GpsCategoryModelTest.kt | 77 ++++++++ 18 files changed, 542 insertions(+), 192 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java create mode 100644 app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt create mode 100644 app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt index f3e00b3f5..39b618718 100644 --- a/app/proguard-rules.txt +++ b/app/proguard-rules.txt @@ -1,5 +1,4 @@ -dontobfuscate -keep class org.apache.http.** { *; } -dontwarn org.apache.http.** --keep class fr.free.nrw.commons.upload.CategoryApi$Page {*;} -keep class android.support.v7.widget.ShareActionProvider { *; } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java index 1b48c3c50..72de0db70 100644 --- a/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java +++ b/app/src/main/java/fr/free/nrw/commons/caching/CacheController.java @@ -7,18 +7,25 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import fr.free.nrw.commons.upload.CategoryApi; +import javax.inject.Inject; +import javax.inject.Singleton; + +import fr.free.nrw.commons.upload.GpsCategoryModel; import timber.log.Timber; +@Singleton public class CacheController { + private final GpsCategoryModel gpsCategoryModel; + private final QuadTree> quadTree; private double x, y; - private QuadTree> quadTree; private double xMinus, xPlus, yMinus, yPlus; private static final int EARTH_RADIUS = 6378137; - public CacheController() { + @Inject + CacheController(GpsCategoryModel gpsCategoryModel) { + this.gpsCategoryModel = gpsCategoryModel; quadTree = new QuadTree<>(-180, -90, +180, +90); } @@ -31,8 +38,8 @@ public class CacheController { public void cacheCategory() { List pointCatList = new ArrayList<>(); - if (CategoryApi.GpsCatExists.getGpsCatExists()) { - pointCatList.addAll(CategoryApi.getGpsCat()); + if (gpsCategoryModel.getGpsCatExists()) { + pointCatList.addAll(gpsCategoryModel.getCategoryList()); Timber.d("Categories being cached: %s", pointCatList); } else { Timber.d("No categories found, so no categories cached"); @@ -65,7 +72,7 @@ public class CacheController { } //Based on algorithm at http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters - public void convertCoordRange() { + private void convertCoordRange() { //Position, decimal degrees double lat = y; double lon = x; diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java index 514b2fb5f..93ddb60d5 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategorizationFragment.java @@ -39,7 +39,7 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import fr.free.nrw.commons.upload.CategoryApi; +import fr.free.nrw.commons.upload.GpsCategoryModel; import fr.free.nrw.commons.utils.StringSortingUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -73,6 +73,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment { @Inject @Named("prefs") SharedPreferences prefsPrefs; @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; @Inject CategoryDao categoryDao; + @Inject GpsCategoryModel gpsCategoryModel; private RVRendererAdapter categoriesAdapter; private OnCategoriesSaveHandler onCategoriesSaveHandler; @@ -253,7 +254,6 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment { } private Observable defaultCategories() { - Observable directCat = directCategories(); if (hasDirectCategories) { Timber.d("Image has direct Cat"); @@ -287,9 +287,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment { } private Observable gpsCategories() { - return Observable.fromIterable( - CategoryApi.GpsCatExists.getGpsCatExists() - ? CategoryApi.getGpsCat() : new ArrayList<>()) + return Observable.fromIterable(gpsCategoryModel.getCategoryList()) .map(name -> new CategoryItem(name, false)); } 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 b0aa3e5e6..f4a77c449 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 @@ -13,7 +13,6 @@ import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; -import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.mwapi.MediaWikiApi; @@ -117,12 +116,6 @@ public class CommonsApplicationModule { return new LocationServiceManager(context); } - @Provides - @Singleton - public CacheController provideCacheController() { - return new CacheController(); - } - @Provides @Singleton public DBOpenHelper provideDBOpenHelper(Context context) { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java new file mode 100644 index 000000000..031796745 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java @@ -0,0 +1,101 @@ +package fr.free.nrw.commons.mwapi; + +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Named; + +import fr.free.nrw.commons.mwapi.model.ApiResponse; +import fr.free.nrw.commons.mwapi.model.Page; +import fr.free.nrw.commons.mwapi.model.PageCategory; +import io.reactivex.Single; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; +import timber.log.Timber; + +/** + * Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates + * with nearby Commons categories. Parses the results using GSON to obtain a list of relevant + * categories. Note: that caller is responsible for executing the request() method on a background + * thread. + */ +public class CategoryApi { + + private final OkHttpClient okHttpClient; + private final HttpUrl mwUrl; + private final Gson gson; + + @Inject + public CategoryApi(OkHttpClient okHttpClient, Gson gson, + @Named("commons_mediawiki_url") HttpUrl mwUrl) { + this.okHttpClient = okHttpClient; + this.mwUrl = mwUrl; + this.gson = gson; + } + + public Single> request(String coords) { + return Single.fromCallable(() -> { + HttpUrl apiUrl = buildUrl(coords); + Timber.d("URL: %s", apiUrl.toString()); + + Request request = new Request.Builder().get().url(apiUrl).build(); + Response response = okHttpClient.newCall(request).execute(); + ResponseBody body = response.body(); + if (body == null) { + return Collections.emptyList(); + } + + ApiResponse apiResponse = gson.fromJson(body.charStream(), ApiResponse.class); + Set categories = new LinkedHashSet<>(); + if (apiResponse != null && apiResponse.hasPages()) { + for (Page page : apiResponse.query.pages) { + for (PageCategory category : page.getCategories()) { + categories.add(category.withoutPrefix()); + } + } + } + return new ArrayList<>(categories); + }); + } + + /** + * Builds URL with image coords for MediaWiki API calls + * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 + * + * @param coords Coordinates to build query with + * @return URL for API query + */ + private HttpUrl buildUrl(String coords) { + return mwUrl.newBuilder() + .addPathSegment("w") + .addPathSegment("api.php") + .addQueryParameter("action", "query") + .addQueryParameter("prop", "categories|coordinates|pageprops") + .addQueryParameter("format", "json") + .addQueryParameter("clshow", "!hidden") + .addQueryParameter("coprop", "type|name|dim|country|region|globe") + .addQueryParameter("codistancefrompoint", coords) + .addQueryParameter("generator", "geosearch") + .addQueryParameter("ggscoord", coords) + .addQueryParameter("ggsradius", "10000") + .addQueryParameter("ggslimit", "10") + .addQueryParameter("ggsnamespace", "6") + .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") + .addQueryParameter("ggsprimary", "all") + .addQueryParameter("formatversion", "2") + .build(); + } + +} + + + diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java new file mode 100644 index 000000000..7feb90251 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/ApiResponse.java @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model; + +public class ApiResponse { + public Query query; + + public ApiResponse() { + } + + public boolean hasPages() { + return query != null && query.pages != null; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java new file mode 100644 index 000000000..d01ba658f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java @@ -0,0 +1,17 @@ +package fr.free.nrw.commons.mwapi.model; + +import android.support.annotation.NonNull; + +public class Page { + public String title; + public PageCategory[] categories; + public PageCategory category; + + public Page() { + } + + @NonNull + public PageCategory[] getCategories() { + return categories != null ? categories : new PageCategory[0]; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java new file mode 100644 index 000000000..be4b9fd79 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/PageCategory.java @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model; + +public class PageCategory { + public String title; + + public PageCategory() { + } + + public String withoutPrefix() { + return title != null ? title.replace("Category:", "") : ""; + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java new file mode 100644 index 000000000..b87f97cc3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java @@ -0,0 +1,10 @@ +package fr.free.nrw.commons.mwapi.model; + +public class Query { + public Page[] pages; + + public Query() { + pages = new Page[0]; + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java b/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java deleted file mode 100644 index 8a7dfcc6e..000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/CategoryApi.java +++ /dev/null @@ -1,165 +0,0 @@ -package fr.free.nrw.commons.upload; - -import android.support.annotation.NonNull; - -import com.google.gson.Gson; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Named; - -import okhttp3.Call; -import okhttp3.Callback; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.ResponseBody; -import timber.log.Timber; - -/** - * Uses the OkHttp library to implement asynchronous calls to the Commons MediaWiki API to match - * GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list - * of relevant categories. - */ -public class CategoryApi { - - private static Set categorySet; - private static List categoryList; - private final OkHttpClient okHttpClient; - private final HttpUrl mwUrl; - private final Gson gson; - - @Inject - public CategoryApi(OkHttpClient okHttpClient, @Named("commons_mediawiki_url") HttpUrl mwUrl, Gson gson) { - this.okHttpClient = okHttpClient; - this.mwUrl = mwUrl; - this.gson = gson; - categorySet = new HashSet<>(); - } - - public static List getGpsCat() { - return categoryList; - } - - public static void setGpsCat(List cachedList) { - categoryList = new ArrayList<>(); - categoryList.addAll(cachedList); - Timber.d("Setting GPS cats from cache: %s", categoryList); - } - - public void request(String coords) { - String apiUrl = buildUrl(coords); - Timber.d("URL: %s", apiUrl); - - Call call = okHttpClient.newCall(new Request.Builder().get().url(apiUrl).build()); - call.enqueue(new Callback() { - @Override - public void onFailure(@NonNull Call call, @NonNull IOException e) { - Timber.e(e); - GpsCatExists.setGpsCatExists(false); - } - - @Override - public void onResponse(@NonNull Call call, @NonNull okhttp3.Response response) { - categoryList = new ArrayList<>(); - categorySet = new HashSet<>(); - ResponseBody body = response.body(); - if (body == null) { - return; - } - QueryResponse queryResponse = gson.fromJson(body.charStream(), QueryResponse.class); - if (queryResponse != null && queryResponse.query != null && queryResponse.query.pages != null) { - for (Page page : queryResponse.query.pages) { - if (page.categories != null) { - for (Category category : page.categories) { - String categoryString = category.title.replace("Category:", ""); - categorySet.add(categoryString); - } - categoryList = new ArrayList<>(categorySet); - } - } - } - GpsCatExists.setGpsCatExists(!categorySet.isEmpty()); - } - }); - } - - /** - * Builds URL with image coords for MediaWiki API calls - * Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2 - * - * @param coords Coordinates to build query with - * @return URL for API query - */ - private String buildUrl(String coords) { - return mwUrl.newBuilder() - .addPathSegment("w") - .addPathSegment("api.php") - .addQueryParameter("action", "query") - .addQueryParameter("prop", "categories|coordinates|pageprops") - .addQueryParameter("format", "json") - .addQueryParameter("clshow", "!hidden") - .addQueryParameter("coprop", "type|name|dim|country|region|globe") - .addQueryParameter("codistancefrompoint", coords) - .addQueryParameter("generator", "geosearch") - .addQueryParameter("ggscoord", coords) - .addQueryParameter("ggsradius", "10000") - .addQueryParameter("ggslimit", "10") - .addQueryParameter("ggsnamespace", "6") - .addQueryParameter("ggsprop", "type|name|dim|country|region|globe") - .addQueryParameter("ggsprimary", "all") - .addQueryParameter("formatversion", "2") - .build().toString(); - } - - public static class GpsCatExists { - private static boolean gpsCatExists; - - public static void setGpsCatExists(boolean gpsCat) { - gpsCatExists = gpsCat; - } - - public static boolean getGpsCatExists() { - return gpsCatExists; - } - } - - private static class QueryResponse { - public Query query; - - public QueryResponse() { - } - } - - private static class Query { - public Page[] pages; - - public Query() { - pages = new Page[0]; - } - } - - private static class Page { - public String title; - public Category[] categories; - public Category category; - - public Page() { - } - } - - private static class Category { - public String title; - - public Category() { - } - } -} - - - diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java new file mode 100644 index 000000000..841210453 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/GpsCategoryModel.java @@ -0,0 +1,40 @@ +package fr.free.nrw.commons.upload; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class GpsCategoryModel { + private Set categorySet; + + @Inject + public GpsCategoryModel() { + clear(); + } + + public void clear() { + categorySet = new HashSet<>(); + } + + public boolean getGpsCatExists() { + return !categorySet.isEmpty(); + } + + public List getCategoryList() { + return new ArrayList<>(categorySet); + } + + public void setCategoryList(List categoryList) { + clear(); + categorySet.addAll(categoryList != null ? categoryList : new ArrayList<>()); + } + + public void add(String categoryString) { + categorySet.add(categoryString); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index e3188534c..98d11c20e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -1,6 +1,7 @@ package fr.free.nrw.commons.upload; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -73,8 +74,10 @@ import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; +import fr.free.nrw.commons.mwapi.CategoryApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import io.reactivex.schedulers.Schedulers; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; @@ -127,6 +130,8 @@ public class ShareActivity @Inject @Named("default_preferences") SharedPreferences prefs; + @Inject + GpsCategoryModel gpsCategoryModel; private String source; private String mimeType; @@ -687,8 +692,9 @@ public class ShareActivity /** * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. - * Then initiates the calls to MediaWiki API through an instance of MwVolleyApi. + * Then initiates the calls to MediaWiki API through an instance of CategpryApi. */ + @SuppressLint("CheckResult") public void useImageCoords() { if (decimalCoords != null) { Timber.d("Decimal coords of image: %s", decimalCoords); @@ -707,12 +713,21 @@ public class ShareActivity // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories if (catListEmpty) { cacheFound = false; - apiCall.request(decimalCoords); + apiCall.request(decimalCoords) + .subscribeOn(Schedulers.io()) + .observeOn(Schedulers.io()) + .subscribe( + gpsCategoryModel::setCategoryList, + throwable -> { + Timber.e(throwable); + gpsCategoryModel.clear(); + } + ); Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList); } else { cacheFound = true; - Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); - CategoryApi.setGpsCat(displayCatList); + Timber.d("Cache found, setting categoryList in model to %s", displayCatList); + gpsCategoryModel.setCategoryList(displayCatList); } }else{ Timber.d("EXIF: no coords"); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 076b3e745..090cf39b5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -8,7 +8,6 @@ import com.nhaarman.mockito_kotlin.mock import com.squareup.leakcanary.RefWatcher import fr.free.nrw.commons.auth.AccountUtil import fr.free.nrw.commons.auth.SessionManager -import fr.free.nrw.commons.caching.CacheController import fr.free.nrw.commons.data.DBOpenHelper import fr.free.nrw.commons.di.CommonsApplicationComponent import fr.free.nrw.commons.di.CommonsApplicationModule @@ -42,7 +41,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() val locationServiceManager: LocationServiceManager = mock() - val cacheController: CacheController = mock() val mockDbOpenHelper: DBOpenHelper = mock() val nearbyPlaces: NearbyPlaces = mock() val lruCache: LruCache = mock() @@ -73,8 +71,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager - override fun provideCacheController(): CacheController = cacheController - override fun provideDBOpenHelper(context: Context): DBOpenHelper = mockDbOpenHelper override fun provideNearbyPlaces(): NearbyPlaces = nearbyPlaces diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt new file mode 100644 index 000000000..76f34d55d --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/CategoryApiTest.kt @@ -0,0 +1,178 @@ +package fr.free.nrw.commons.mwapi + +import com.google.gson.Gson +import fr.free.nrw.commons.mwapi.model.Page +import fr.free.nrw.commons.mwapi.model.PageCategory +import okhttp3.HttpUrl +import okhttp3.OkHttpClient +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.MockWebServer +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test + +class CategoryApiTest { + private lateinit var server: MockWebServer + private lateinit var url: String + private lateinit var categoryApi: CategoryApi + + @Before + fun setUp() { + server = MockWebServer() + url = "http://${server.hostName}:${server.port}/" + categoryApi = CategoryApi(OkHttpClient.Builder().build(), Gson(), HttpUrl.parse(url)) + } + + @After + fun teardown() { + server.shutdown() + } + + @Test + fun apiReturnsEmptyListWhenError() { + server.enqueue(MockResponse().setResponseCode(400).setBody("")) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenTheresNoQuery() { + server.success(emptyMap()) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenQueryHasNoPages() { + server.success(mapOf("query" to emptyMap())) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun apiReturnsEmptyWhenQueryHasPagesButTheyreEmpty() { + server.success(mapOf("query" to + mapOf("pages" to emptyList()))) + + assertTrue(categoryApi.request("foo").blockingGet().isEmpty()) + } + + @Test + fun singlePageSingleCategory() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(1, response.size) + assertEquals("one", response[0]) + } + + @Test + fun multiplePagesSingleCategory() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one")), + page(listOf("two")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(2, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + } + + @Test + fun singlePageMultipleCategories() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(2, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + } + + @Test + fun multiplePagesMultipleCategories() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two")), + page(listOf("three", "four")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(4, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + assertEquals("three", response[2]) + assertEquals("four", response[3]) + } + + @Test + fun multiplePagesMultipleCategories_duplicatesRemoved() { + server.success(mapOf("query" to + mapOf("pages" to listOf( + page(listOf("one", "two", "three")), + page(listOf("three", "four", "one")) + )))) + + val response = categoryApi.request("foo").blockingGet() + + assertEquals(4, response.size) + assertEquals("one", response[0]) + assertEquals("two", response[1]) + assertEquals("three", response[2]) + assertEquals("four", response[3]) + } + + @Test + fun requestSendsWhatWeExpect() { + server.success(mapOf("query" to mapOf("pages" to emptyList()))) + + val coords = "foo,bar" + categoryApi.request(coords).blockingGet() + + server.takeRequest().let { request -> + assertEquals("GET", request.method) + assertEquals("/w/api.php", request.requestUrl.encodedPath()) + request.requestUrl.let { url -> + assertEquals("query", url.queryParameter("action")) + assertEquals("categories|coordinates|pageprops", url.queryParameter("prop")) + assertEquals("json", url.queryParameter("format")) + assertEquals("!hidden", url.queryParameter("clshow")) + assertEquals("type|name|dim|country|region|globe", url.queryParameter("coprop")) + assertEquals(coords, url.queryParameter("codistancefrompoint")) + assertEquals("geosearch", url.queryParameter("generator")) + assertEquals(coords, url.queryParameter("ggscoord")) + assertEquals("10000", url.queryParameter("ggsradius")) + assertEquals("10", url.queryParameter("ggslimit")) + assertEquals("6", url.queryParameter("ggsnamespace")) + assertEquals("type|name|dim|country|region|globe", url.queryParameter("ggsprop")) + assertEquals("all", url.queryParameter("ggsprimary")) + assertEquals("2", url.queryParameter("formatversion")) + } + } + } + + private fun page(catList: List) = Page().apply { + categories = catList.map { + PageCategory().apply { + title = "Category:$it" + } + }.toTypedArray() + } +} + +fun MockWebServer.success(response: Map) { + enqueue(MockResponse().setResponseCode(200).setBody(Gson().toJson(response))) +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt new file mode 100644 index 000000000..41406a894 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/ApiResponseTest.kt @@ -0,0 +1,28 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.* +import org.junit.Test + +class ApiResponseTest { + @Test + fun hasPages_whenQueryIsNull() { + val response = ApiResponse() + assertFalse(response.hasPages()) + } + + @Test + fun hasPages_whenPagesIsNull() { + val response = ApiResponse() + response.query = Query() + response.query.pages = null + assertFalse(response.hasPages()) + } + + @Test + fun hasPages_defaultsToSafeValue() { + val response = ApiResponse() + response.query = Query() + assertNotNull(response.query.pages) + assertTrue(response.hasPages()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt new file mode 100644 index 000000000..fcc3d408f --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageCategoryTest.kt @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.assertEquals +import org.junit.Test + +class PageCategoryTest { + @Test + fun stripPrefix_whenPresent() { + val testObject = PageCategory() + testObject.title = "Category:Foo" + assertEquals("Foo", testObject.withoutPrefix()) + } + + @Test + fun stripPrefix_prefixAbsent() { + val testObject = PageCategory() + testObject.title = "Foo_Bar" + assertEquals("Foo_Bar", testObject.withoutPrefix()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt new file mode 100644 index 000000000..4179b4fb5 --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/model/PageTest.kt @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.mwapi.model + +import org.junit.Assert.assertNotNull +import org.junit.Test + +class PageTest { + @Test + fun categoriesDefaultToSafeValue() { + val page = Page() + assertNotNull(page.getCategories()) + } +} \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt new file mode 100644 index 000000000..cd2d77ada --- /dev/null +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/GpsCategoryModelTest.kt @@ -0,0 +1,77 @@ +package fr.free.nrw.commons.upload + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +class GpsCategoryModelTest { + + private lateinit var testObject : GpsCategoryModel + + @Before + fun setUp() { + testObject = GpsCategoryModel() + } + + @Test + fun initiallyTheModelIsEmpty() { + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + } + + @Test + fun addingCategoriesToTheModel() { + testObject.add("one") + assertTrue(testObject.gpsCatExists) + assertFalse(testObject.categoryList.isEmpty()) + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun duplicatesAreIgnored() { + testObject.add("one") + testObject.add("one") + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun modelProtectsAgainstExternalModification() { + testObject.add("one") + + val list = testObject.categoryList + list.add("two") + + assertEquals(listOf("one"), testObject.categoryList) + } + + @Test + fun clearingTheModel() { + testObject.add("one") + + testObject.clear() + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + + testObject.add("two") + assertEquals(listOf("two"), testObject.categoryList) + } + + @Test + fun settingTheListHandlesNull() { + testObject.add("one") + + testObject.categoryList = null + + assertFalse(testObject.gpsCatExists) + assertTrue(testObject.categoryList.isEmpty()) + } + + @Test + fun setttingTheListOverwritesExistingValues() { + testObject.add("one") + + testObject.categoryList = listOf("two") + + assertEquals(listOf("two"), testObject.categoryList) + } +} \ No newline at end of file From e2557f5081bdc4ce3423685700c568c55e565270 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 19:22:10 +1000 Subject: [PATCH 37/94] Fix conflicts in ShareActivity.java --- .../nrw/commons/upload/ShareActivity.java | 91 +------------------ 1 file changed, 4 insertions(+), 87 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index e512cb889..b3fa54e9e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -347,19 +347,12 @@ public class ShareActivity if (mediaUri != null) { backgroundImageView.setImageURI(mediaUri); } -<<<<<<< HEAD } /** * Initialize views and setup listeners here for FAB to prevent cluttering onCreate */ private void initViewsAndListeners() { - //Main FAB splits into Zoom and Map - mainFab = (FloatingActionButton) findViewById(R.id.main_fab); - zoomInButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_in); - zoomOutButton = (FloatingActionButton) findViewById(R.id.media_upload_zoom_out); - maps_fragment = (FloatingActionButton) findViewById(R.id.media_map); - mainFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -382,11 +375,11 @@ public class ShareActivity Timber.e(e); } - maps_fragment.setVisibility(View.VISIBLE); + mapsFragment.setVisibility(View.VISIBLE); if( imageObj == null || imageObj.imageCoordsExists){ - maps_fragment.setVisibility(View.INVISIBLE); + mapsFragment.setVisibility(View.INVISIBLE); } - maps_fragment.setOnClickListener(new View.OnClickListener() { + mapsFragment.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if( imageObj != null && imageObj.imageCoordsExists) { @@ -399,90 +392,14 @@ public class ShareActivity }); } - -======= - if (savedInstanceState != null) { - contribution = savedInstanceState.getParcelable("contribution"); - } - - requestAuthToken(); - - Timber.d("Uri: %s", mediaUri.toString()); - Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); - - useNewPermissions = false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - useNewPermissions = true; - - if (!needsToRequestStoragePermission()) { - storagePermitted = true; - } - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - locationPermitted = true; - } - } - - // Check storage permissions if marshmallow or newer - if (useNewPermissions && (!storagePermitted || !locationPermitted)) { - if (!storagePermitted && !locationPermitted) { - String permissionRationales = - getResources().getString(R.string.read_storage_permission_rationale) + "\n" - + getResources().getString(R.string.location_permission_rationale); - snackbar = requestPermissionUsingSnackBar( - permissionRationales, - new String[]{ - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION); - View snackbarView = snackbar.getView(); - TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text); - textView.setMaxLines(3); - } else if (!storagePermitted) { - requestPermissionUsingSnackBar( - getString(R.string.read_storage_permission_rationale), - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, - REQUEST_PERM_ON_CREATE_STORAGE); - } else if (!locationPermitted) { - requestPermissionUsingSnackBar( - getString(R.string.location_permission_rationale), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - REQUEST_PERM_ON_CREATE_LOCATION); - } - } - performPreUploadProcessingOfFile(); - - - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); - categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); - if (shareView == null && categorizationFragment == null) { - shareView = new SingleUploadFragment(); - getSupportFragmentManager() - .beginTransaction() - .add(R.id.single_upload_fragment_container, shareView, "shareView") - .commitAllowingStateLoss(); - } - uploadController.prepareService(); - mapsFragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists != true){ - mapsFragment.setVisibility(View.INVISIBLE); - } - - } - - /* ->>>>>>> refs/remotes/commons-app/master + /** * Function to display the zoom and map FAB */ private void showFABMenu() { isFABOpen=true; -<<<<<<< HEAD if( imageObj != null && imageObj.imageCoordsExists) - maps_fragment.setVisibility(View.VISIBLE); -======= - if( imageObj != null && imageObj.imageCoordsExists == true) mapsFragment.setVisibility(View.VISIBLE); ->>>>>>> refs/remotes/commons-app/master zoomInButton.setVisibility(View.VISIBLE); mainFab.animate().rotationBy(180); From 02a735f2c0af2d8ae1f286d47c7a7bd91d8ebfcd Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 19:24:58 +1000 Subject: [PATCH 38/94] Rearrange member variables --- .../nrw/commons/upload/ShareActivity.java | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index b3fa54e9e..1e33bcddf 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -90,6 +90,20 @@ public class ShareActivity implements SingleUploadFragment.OnUploadActionInitiated, OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse { + @Inject + MediaWikiApi mwApi; + @Inject + CacheController cacheController; + @Inject + SessionManager sessionManager; + @Inject + UploadController uploadController; + @Inject + ModifierSequenceDao modifierSequenceDao; + @Inject + @Named("default_preferences") + SharedPreferences prefs; + @BindView(R.id.container) FrameLayout flContainer; @BindView(R.id.backgroundImage) @@ -105,30 +119,15 @@ public class ShareActivity @BindView(R.id.expanded_image) PhotoView expandedImageView; - private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; - private CategorizationFragment categorizationFragment; - - @Inject - MediaWikiApi mwApi; - @Inject - CacheController cacheController; - @Inject - SessionManager sessionManager; - @Inject - UploadController uploadController; - @Inject - ModifierSequenceDao modifierSequenceDao; - @Inject - @Named("default_preferences") - SharedPreferences prefs; - + private String source; private String mimeType; + private CategorizationFragment categorizationFragment; private Uri mediaUri; private Contribution contribution; private boolean cacheFound; From e3ba7403c4d28269608e9154198ad30e8115162a Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Fri, 25 May 2018 11:42:49 +0200 Subject: [PATCH 39/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 2 - app/src/main/res/values-ast/strings.xml | 2 - app/src/main/res/values-b+sr+Latn/strings.xml | 2 - app/src/main/res/values-ba/strings.xml | 2 - app/src/main/res/values-bg/strings.xml | 2 - app/src/main/res/values-bn/strings.xml | 2 - app/src/main/res/values-br/strings.xml | 2 - app/src/main/res/values-bs/strings.xml | 2 - app/src/main/res/values-ca/strings.xml | 3 +- app/src/main/res/values-cs/strings.xml | 2 - app/src/main/res/values-csb/strings.xml | 2 - app/src/main/res/values-cy/strings.xml | 2 - app/src/main/res/values-da/strings.xml | 2 - app/src/main/res/values-de/strings.xml | 3 +- app/src/main/res/values-diq/strings.xml | 2 - app/src/main/res/values-el/strings.xml | 2 - app/src/main/res/values-es/strings.xml | 4 +- app/src/main/res/values-eu/strings.xml | 2 - app/src/main/res/values-fa/strings.xml | 2 - app/src/main/res/values-fi/error.xml | 3 +- app/src/main/res/values-fi/strings.xml | 10 +++-- app/src/main/res/values-fo/strings.xml | 2 - app/src/main/res/values-fr/strings.xml | 3 +- app/src/main/res/values-frr/strings.xml | 2 - app/src/main/res/values-gl/strings.xml | 2 - app/src/main/res/values-haw/strings.xml | 2 - app/src/main/res/values-hi/strings.xml | 2 - app/src/main/res/values-hrx/strings.xml | 2 - app/src/main/res/values-hsb/strings.xml | 2 - app/src/main/res/values-hu/strings.xml | 2 - app/src/main/res/values-in/strings.xml | 2 - app/src/main/res/values-is/strings.xml | 2 - app/src/main/res/values-it/strings.xml | 2 - app/src/main/res/values-iw/strings.xml | 3 +- app/src/main/res/values-ja/strings.xml | 2 - app/src/main/res/values-jv/strings.xml | 2 - app/src/main/res/values-ka/strings.xml | 2 - app/src/main/res/values-kab/strings.xml | 2 - app/src/main/res/values-km/strings.xml | 2 - app/src/main/res/values-ko-rKP/strings.xml | 2 - app/src/main/res/values-ko/strings.xml | 2 - app/src/main/res/values-kum/strings.xml | 3 +- app/src/main/res/values-ky/strings.xml | 2 - app/src/main/res/values-lb/strings.xml | 2 - app/src/main/res/values-li/strings.xml | 2 - app/src/main/res/values-lt/strings.xml | 2 - app/src/main/res/values-mk/strings.xml | 3 +- app/src/main/res/values-ml/strings.xml | 2 - app/src/main/res/values-mr/strings.xml | 2 - app/src/main/res/values-ms/strings.xml | 2 - app/src/main/res/values-nb/strings.xml | 2 - app/src/main/res/values-ne/strings.xml | 2 - app/src/main/res/values-nl/strings.xml | 2 - app/src/main/res/values-oc/strings.xml | 2 - app/src/main/res/values-or/strings.xml | 2 - app/src/main/res/values-pa/strings.xml | 2 - app/src/main/res/values-pl/strings.xml | 2 - app/src/main/res/values-pms/strings.xml | 3 +- app/src/main/res/values-ps/strings.xml | 2 - app/src/main/res/values-pt-rBR/strings.xml | 3 +- app/src/main/res/values-pt/strings.xml | 2 - app/src/main/res/values-qq/strings.xml | 2 - app/src/main/res/values-ro/strings.xml | 2 - app/src/main/res/values-ru/strings.xml | 3 +- app/src/main/res/values-sd/strings.xml | 2 - app/src/main/res/values-si/strings.xml | 2 - app/src/main/res/values-sk/strings.xml | 2 - app/src/main/res/values-sr/strings.xml | 2 - app/src/main/res/values-su/strings.xml | 2 - app/src/main/res/values-sv/strings.xml | 3 +- app/src/main/res/values-te/strings.xml | 2 - app/src/main/res/values-th/strings.xml | 2 - app/src/main/res/values-tr/strings.xml | 2 - app/src/main/res/values-ug/strings.xml | 2 - app/src/main/res/values-uk/strings.xml | 6 +-- app/src/main/res/values-ur/strings.xml | 2 - app/src/main/res/values-vi/strings.xml | 38 +++++++++++++------ app/src/main/res/values-xmf/strings.xml | 2 - app/src/main/res/values-zh-rTW/strings.xml | 3 +- app/src/main/res/values-zh/strings.xml | 2 - 80 files changed, 50 insertions(+), 172 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index a9127c1eb..b8e98ac40 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -54,8 +54,6 @@ العنوان الوصف لا يمكن تسجيل الدخول - فشل في شبكة الاتصال - لا يمكن تسجيل الدخول - فضلا تحقق من اسم المستخدم - لا يمكن تسجيل الدخول - فضلا تحقق من كلمة السر الكثير من المحاولات غير الناجحة. الرجاء المحاولة مرة أخرى في بضع دقائق. عذراً، لقد تم منع هذا المستخدم على كومنز يجب توفير رمز التحقق المزدوج. diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 947bc441a..e92f5dc0a 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -49,8 +49,6 @@ Apurre un títulu pa esti ficheru Descripción Nun se pudo aniciar sesión – error de rede - Nun se pudo aniciar sesión – por favor compruebe\'l so nome d\'usuariu - Nun se pudo aniciar sesión – por favor compruebe la so contraseña Demasiaos intentos incorreutos. Téntalo otra vuelta n\'unos minutos. Sentímoslo, esti usuariu ta bloquiáu en Commons Tienes de dar el códigu d\'identificación de dos factores. diff --git a/app/src/main/res/values-b+sr+Latn/strings.xml b/app/src/main/res/values-b+sr+Latn/strings.xml index 0cd72a167..81b826d36 100644 --- a/app/src/main/res/values-b+sr+Latn/strings.xml +++ b/app/src/main/res/values-b+sr+Latn/strings.xml @@ -43,8 +43,6 @@ Naslov Opis Ne mogu da vas prijavim – mreža ne radi - Ne mogu da vas prijavim – proverite svoje korisničko ime - Ne mogu da vas prijavim – proverite svoju lozinku Previše neuspešnih pokušaja. Probajte ponovo za nekoliko minuta. Nažalost, korisnik je blokiran na Ostavi Morate uneti svoj dvofaktorski kod za autentifikaciju. diff --git a/app/src/main/res/values-ba/strings.xml b/app/src/main/res/values-ba/strings.xml index 293dd3f73..f54a93831 100644 --- a/app/src/main/res/values-ba/strings.xml +++ b/app/src/main/res/values-ba/strings.xml @@ -50,8 +50,6 @@ Был файлдың атамаһын күрһәт Тасуирлама Инеп булмай - интернет хатаһы - Инмәнең - ҡулланыусы исемеңде тикшер - Инмәнең - серһуҙеңде тикшер Күп тапҡыр яңылыштың. Зинһар, бер-нисә минуттан тағы ла инеп ҡара Ғәфү итегеҙ, әммә был исемдәге ҡатнашыусыға Викискладҡа инеү тыйылған Ике тапҡыр раҫлай торған шәхси кодты яҙырға кәрәк diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml index cfaf021f9..28b092add 100644 --- a/app/src/main/res/values-bg/strings.xml +++ b/app/src/main/res/values-bg/strings.xml @@ -32,8 +32,6 @@ Заглавие Описание Неуспешно влизане – проблем в мрежата - Неуспешно влизане – моля проверете потребителското си име - Неуспешно влизане – моля проверете паролата си Качване Изменения Качване diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml index 7c76ebaea..69bb5fc33 100644 --- a/app/src/main/res/values-bn/strings.xml +++ b/app/src/main/res/values-bn/strings.xml @@ -57,8 +57,6 @@ এই ফাইলটির জন্য একটি শিরোনাম প্রদান করুন বিবরণ প্রবেশ করা যাচ্ছে না - নেটওয়ার্ক ব্যর্থতা - প্রবেশ করা যাচ্ছে না - অনুগ্রহ করে আপনার ব্যবহারকারী নাম পরীক্ষা করুন। - প্রবেশ করা যাচ্ছে না - অনুগ্রহ করে আপনার পাসওয়ার্ড পরীক্ষা করুন খুব বেশি অসফল প্রচেষ্টা। অনুগ্রহ করে কয়েক মিনিট পরে আবারও চেষ্টা করুন। দুঃখিত, এই ব্যবহারকারীকে কমন্সে বাধা দেয়া হয়েছে অাপনাকে অবশ্যই অাপনার দু\'স্তরের সত্যায়নকরণ কোড দিতে হবে। diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml index 8383f7cbe..2a7cd2284 100644 --- a/app/src/main/res/values-br/strings.xml +++ b/app/src/main/res/values-br/strings.xml @@ -52,8 +52,6 @@ Roit un titl d\'ar restr-mañ, mar plij Deskrivadur Ne c\'haller ket kevreañ - rouedad sac\'het - Ne c\'haller ket kevreañ - Gwiriit hoc\'h anv implijer, mar plij - Ne c\'haller ket kevreañ - Gwiriit ho ker tremen, mar plij Re a daolioù-esae. Klaskit en-dro a-benn ur pennadig amzer. Hon digarezit, prennet eo bet an implijer-mañ e Commons Rankout a rit reiñ ho kod dilesa gant daou faktor. diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml index 6c0097089..e6667a083 100644 --- a/app/src/main/res/values-bs/strings.xml +++ b/app/src/main/res/values-bs/strings.xml @@ -43,8 +43,6 @@ Naslov Opis Ne mogu Vas prijaviti – mreža ne radi - Ne mogu Vas prijaviti – provjerite svoje korisničko ime - Ne mogu Vas prijaviti – provjerite svoje lozinku Napravili ste previše grešaka u prijavi. Pokušajte ponovo za nekoliko minuta. Žao nam je, korisnik je blokiran na Commonsu Morate upisati kôd za potvrdu u 2 koraka. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index f7f40f427..aaf5ca737 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -50,8 +50,6 @@ Títol Descripció No s\'ha pogut iniciar la sessió – error de xarxa - No s\'ha pogut iniciar la sessió – si et plau comprova el teu nom d\'usuari - No s’ha pogut iniciar la sessió. Comproveu la vostra contrasenya Massa intents erronis – Proveu-ho de nou d\'aquí uns minuts. Ho sentim, aquest usuari ha estat blocat a Commons Heu de proporcionar el vostre codi d\'autenticació de dos factors. @@ -192,4 +190,5 @@ Gràcies per fer una modificació %1$s us ha mencionat a %2$s. Preguntes freqüents + No s’ha trobat cap imatge. diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 6e2453d6d..eb87ab83e 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -61,8 +61,6 @@ Vložte prosím název tohoto souboru Popis Nelze se přihlásit - selhání sítě - Nelze se přihlásit - prosím zkontrolujte své uživatelské jméno - Nelze se přihlásit - zkontrolujte prosím své heslo Příliš mnoho neúspěšných pokusů. Zkuste to prosím znovu za několik minut. Omlouváme se, tento uživatel byl na Commons zablokován Prosím vložte kód pro své dvoufázové ověření. diff --git a/app/src/main/res/values-csb/strings.xml b/app/src/main/res/values-csb/strings.xml index 2d7b99904..f04df2055 100644 --- a/app/src/main/res/values-csb/strings.xml +++ b/app/src/main/res/values-csb/strings.xml @@ -41,8 +41,6 @@ Titel Òpisënk Ni mòże sã wlogòwac - fela sécë - Ni mòże sã wlogòwac - sprôwdzë miono brëkòwnika - Ni mòże sã wlogòwac - sprôwdzë parolã Za wiele nieùdałich prób wlogòwaniô. Spróbùjë znowa za czile minut. Nen brëkòwnik òstôł zablokòwóny na Commons Mùszisz wpisac swój kòd dlô dwafaktorowi aùtorizacëji. diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index ef56bc3b0..ca00d1c4d 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -43,8 +43,6 @@ Teitl Disgrifiad Yn methu mewngofnodi - methodd y rhwydwaith - Yn methu mewngofnodi - gwirwch eich enw defnyddiwr - Yn methu mewngofnodi - gwirwch eich cyfrinair Cafwyd gormod o ymgeision aflwyddiannus. Oedwch ennyd cyn ceisio eto. Ymddiheurwn. Mae\'r defnyddiwr hwn wedi ei flocio ar Gomin Wikimedia Mae\'n rhaid i chi roi eich cod adnabod 2 ffactor. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index e10a93850..fe194ce94 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -54,8 +54,6 @@ Angiv venligt en titel for denne fil Beskrivelse Kan ikke logge på - netværksfejl - Ude af stand til at logge på - tjek venligst dit brugernavn - Ude af stand til at logge på - tjek venligst din adgangskode Alt for mange mislykkede forsøg. Prøv igen om et par minutter. Beklager, denne bruger er blevet blokeret på Commons Du skal angive din tofaktorgodkendelseskode. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 9f47ce42e..6fbd7d517 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -53,8 +53,7 @@ Bitte gib einen Titel für diese Datei an Beschreibung Anmeldung fehlgeschlagen – Netzwerkfehler - Anmeldung fehlgeschlagen – Bitte Benutzernamen überprüfen - Anmeldung fehlgeschlagen – Bitte Passwort überprüfen + Anmeldung fehlgeschlagen. Bitte Benutzernamen und Passwort überprüfen. Zu viele erfolglose Versuche. Bitte in einigen Minuten erneut versuchen. Dieser Benutzer wurde leider auf Commons gesperrt Du musst deinen Code zur Zwei-Faktor-Authentifizierung angeben. diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml index 53bd88bac..79da245fe 100644 --- a/app/src/main/res/values-diq/strings.xml +++ b/app/src/main/res/values-diq/strings.xml @@ -52,8 +52,6 @@ Sername Şınasnayış Xırabiya kewten-network xeta - Ronıştışo abeno - Namey karberi ye xo kontrol kerë - Ronıştışo nêabeno - Parolay xo kontrol kerë Şıma xeylê rayi kerd ke cı kewê, a ser nêvıst. Şıma rê zehmet 2-3 deqey ra tepeya reyna bıcerrebnên. Qısur mewni rê, Karber commons dı bloqe biyo. Nidekeweya de diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index e372df836..14444cc4a 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -57,8 +57,6 @@ Παρακαλώ παρέχετε ένα τίτλο για αυτό το αρχείο Περιγραφή Δεν είναι δυνατή η σύνδεση - αποτυχία του δικτύου - Δεν είναι δυνατή η σύνδεση - ελέγξτε το όνομα χρήστη σας - Δεν είναι δυνατή η σύνδεση - παρακαλούμε ελέγξτε τον κωδικό σας Πάρα πολλές ανεπιτυχείς προσπάθειες. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά. Συγνώμη, αυτός ο χρήστης έχει αποκλειστεί από τα Commons Πρέπει να δώσετε τον κωδικό πιστοποίησης με δύο παράγοντες diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 9e5dae650..804c8755d 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1,6 +1,7 @@ - Commons app on kaatunut + Commons on kaatunut Pahoittelemme, virhe tapahtui. Kerro meille mitä teit äsken, sähköpostitse. Se auttaa meitä korjaamaan ongelman! Kiitos! diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index e833932a8..b0ea9ce0f 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -58,8 +58,6 @@ Anna tälle tiedostolle otsikko Kuvaus Kirjautuminen epäonnistui - verkkovirhe - Kirjautuminen epäonnistui - tarkista käyttäjätunnus - Kirjautuminen epäonnistui - tarkista salasanasi Liikaa epäonnistuneita yrityksiä. Yritä uudelleen parin minuutin kuluttua. Pahoittelut, tämä käyttäjä on estetty Commonsissa Anna kaksivaiheisen tunnistuksen koodi. @@ -162,8 +160,8 @@ Ei kuvausta Tuntematon lisenssi Päivitä - Vaadittu oikeus: Ulkoisen tallennustilan luku. Appi ei voi päästä galleriaasi ilman tätä oikeutta. - Vaadittava lupa: Kirjoita ulkoiseen tallennustilaan. Appi ei voi päästä kameraasi ilman tätä oikeutta. + Vaadittu oikeus: Ulkoisen tallennustilan luku. Sovellus ei voi päästä galleriaasi ilman tätä oikeutta. + Vaadittu oikeus: Kirjoita ulkoiseen tallennustilaan. Sovellus ei voi päästä kameraasi ilman tätä oikeutta. Valinnainen lupa: Saada tämänhetkinen sijainti loukkasuosituksia varten. OK Lähellä olevat paikat @@ -262,4 +260,8 @@ Jatka Peruuta Yritä uudelleen + Selvä! + Kuvia ei löytynyt! + Tallentanut: %1$s + Jaa sovellus diff --git a/app/src/main/res/values-fo/strings.xml b/app/src/main/res/values-fo/strings.xml index c4649407a..58e26797b 100644 --- a/app/src/main/res/values-fo/strings.xml +++ b/app/src/main/res/values-fo/strings.xml @@ -34,8 +34,6 @@ Heiti Frágreiðing Ómøguligt at rita inn - feilur í netsambandinum - Ómøguligt at rita inn - vinarliga eftirkanna títt brúkaranavn - Ómøguligt at rita inn - vinarliga kanna eftir, um títt loyniorð er rætt Ov nógv miseydnaðar royndir. Vinarliga royn aftur um fáir minuttir Haldið okkum tilgóðar, hesin brúkari er blivin sperraður á Commons Login miseydnaðist diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index bfb14baf1..a93f3011d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -65,8 +65,7 @@ Veuillez donner un titre à ce fichier Description Impossible de se connecter — panne de réseau - Impossible de se connecter — veuillez vérifier votre nom d’utilisateur - Impossible de se connecter — veuillez vérifier votre mot de passe + Impossible de se connecter — veuillez vérifier votre nom d’utilisateur et votre mot de passe Trop de tentatives infructueuses. Veuillez réessayer dans quelques minutes. Désolé, cet utilisateur a été bloqué dans Commons Vous devez fournir votre code d’authentification à deux facteurs. diff --git a/app/src/main/res/values-frr/strings.xml b/app/src/main/res/values-frr/strings.xml index f4866e705..1355096c5 100644 --- a/app/src/main/res/values-frr/strings.xml +++ b/app/src/main/res/values-frr/strings.xml @@ -42,8 +42,6 @@ Tiitel Beskriiwang Bi\'t uunmeldin as wat skiaf gingen - näätwerk-feeler - Bi\'t uunmeldin as wat skiaf gingen - luke ans efter di brükernööm - Bi\'t uunmeldin as wat skiaf gingen - luke ans efter det paaswurd Tu fölsis fersoocht saner lok. Ferschük det uun hög minüüten noch ans nei. Didiar brüker as üüb Commons speret wurden. Dü skel dan code för\'t tau-straal-gudkäänen (2FA) uundu. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 630311db4..1dcd7ecca 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -55,8 +55,6 @@ Por favor, proporcione un título para este ficheiro Descrición Erro ao acceder ao sistema: Fallou a rede - Erro ao acceder ao sistema: Comprobe o seu nome de usuario - Erro ao acceder ao sistema: Comprobe o seu contrasinal Demasiados intentos incorrectos. Inténteo de novo nuns minutos. Sentímolo, este usuario está bloqueado en Commons Debe proporcionar o seu código de autenticación de dous factores. diff --git a/app/src/main/res/values-haw/strings.xml b/app/src/main/res/values-haw/strings.xml index a81ab120d..674dc6dbe 100644 --- a/app/src/main/res/values-haw/strings.xml +++ b/app/src/main/res/values-haw/strings.xml @@ -34,8 +34,6 @@ Poʻo Inoa Hōʻike ʻAno Hiki ʻole ke ʻeʻe - hāʻule pūnaewele - Hiki ʻole ke ʻeʻe - hōʻoiaʻiʻo i kāu inoa mea hoʻohana ke ʻoluʻolu - Hiki ʻole ke ʻeʻe - hōʻoiaʻiʻo i kāu ʻōlelo hūnā ke ʻoluʻolu Hoʻāʻo ʻeʻe ʻole he nui kā. E ʻoluʻolu, e hana hou i ka wā hou aku E kala mai, ua pale ʻia kēia mea hoʻohana ma ke Kahilehulehu Hāʻule ka ʻeʻena diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 69bb67655..0404cacd2 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -53,8 +53,6 @@ शीर्षक विवरण प्रवेश नहीं हो रहा - नेटवर्क विफल - प्रवेश नहीं हो रहा - कृपया अपना सदस्य नाम जाँचें - प्रवेश नहीं हो रहा - कृपया अपना पासवर्ड जाँचें ढेर सारे असफल प्रयास होने के कारण कुछ मिनटों के बाद प्रयास करें। क्षमा करें, यह सदस्य कॉमन्स में अवरोधित है आपको अपना दो कारक प्रमाणन कोड प्रदान करना होगा। diff --git a/app/src/main/res/values-hrx/strings.xml b/app/src/main/res/values-hrx/strings.xml index 7905a92d7..b6dbf026d 100644 --- a/app/src/main/res/values-hrx/strings.xml +++ b/app/src/main/res/values-hrx/strings.xml @@ -35,8 +35,6 @@ Titel Beschreibung Oonmeldung fehlgeschlooht – Netzwerrekfehler - Oonmeldung fehlgeschlooht – Bittschön Benutzernoome üwerprüfe - Oonmeldung fehlgeschlooht – Bittschön Passwort üwerprüfe Zu viele erfollichlose Versuche. Bittschön in en poor Minute wieder erneit versuche. Der Benutzer woard leider uff Commons gesperrt Oonmeldung fehlgeschlooht diff --git a/app/src/main/res/values-hsb/strings.xml b/app/src/main/res/values-hsb/strings.xml index 97b0d1af5..a8647fb56 100644 --- a/app/src/main/res/values-hsb/strings.xml +++ b/app/src/main/res/values-hsb/strings.xml @@ -34,8 +34,6 @@ Titul Wopisanje Přizjewjenje je so njeporadźiło - syćowy zmylk - Přizjewjenje je njemóžno - prošu přepruwuj swoje wužiwarske mjeno - Přizjewjenje njeje móžno - prošu přepruwuj swoje hesło Přewjele njewuspěšnych pospytow. Prošu spytaj za něšto mjeńšin hišće raz. Tutoho wužiwarja su bohužel na Commons zablokowali Přizjewjenje je so njeporadźiło diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index d9580dca1..fe91781d9 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -59,8 +59,6 @@ Kérlek, adj címet a fájlnak Leírás Nem lehet bejelentkezni - hálózati hiba - Nem lehet bejelentkezni - ellenőrizd a felhasználóneved - Nem lehet bejelentkezni - ellenőrizd a jelszavad Túl sok sikertelen próbálkozás. Próbálkozz újra pár perc múlva. Sajnáljuk, ezt a felhasználót blokkolták a Commonson Meg kell adnia a kétlépcsős hitelesítő kódját. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 16185f35d..6a533ef67 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -46,8 +46,6 @@ Judul Deskripsi Tidak dapat login - kesalahan pada jaringan - Tidak dapat masuk log - harap periksa nama pengguna Anda - Tidak dapat masuk log - harap periksa kata sandi Anda Terlalu banyak usaha yang gagal. Harap coba lagi dalam beberapa menit Maaf, pengguna ini telah diblokir di Commons Gagal masuk log diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index f1b020965..985e858bf 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -49,8 +49,6 @@ Gefðu þessari skrá einhvern titil Lýsing Innskráning mistókst - bilun í neti - Innskráning mistókst. Athugaðu notandanafnið þitt - Innskráning mistókst. Athugaðu lykilorðið þitt Of margar misteknar tilraunir. Reyndu aftur eftir nokkrar mínútur. Því miður, þessi notandi hefur verið bannaður á Commons Þú verður að setja inn tveggja-þrepa auðkenningarkóðann þinn. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 06bfa701f..30a02428e 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -53,8 +53,6 @@ Titolo Descrizione Impossibile effettuare l\'accesso - errore di rete - Impossibile effettuare l\'accesso - controlla il nome utente - Impossibile effettuare l\'accesso - controlla la password Troppi tentativi falliti. Riprova tra alcuni minuti. Spiacente, questo utente è stato bloccato su Commons Devi fornire il tuo codice di autenticazione a due fattori. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 9ba67e4ee..03a76a3fc 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -58,8 +58,7 @@ נא לתת כותרת לקובץ הזה תיאור לא ניתן להיכנס – כשל בתקשורת - לא ניתן להיכנס – נא לבדוק את שם המשתמש שלך - לא ניתן להיכנס – נא לבדוק את הססמה שלך + לא ניתן להיכנס לחשבון – נא לבדוק את שם המשתמש ואת הסיסמה יותר מדי ניסיונות כושלים להיכנס. נא לנסות שוב בעוד מספר דקות. סליחה, החשבון הזה חסום בוויקישיתוף יש לספק את קוד האימות הדו־שלבי שלך. diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6ff03ee0d..78127106f 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -58,8 +58,6 @@ ファイル名をつけてください 説明 ログインできません - ネットワークのエラーです - ログインできません - 利用者名を確認してください - ログインできません - パスワードを確認してください 失敗した回数が多すぎます。数分待ってからもう一度お試しください。 申し訳ありませんが、この利用者はコモンズでブロックされています。 2要素認証コードを提供する必要があります。 diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml index e051b6f97..a4a85c80f 100644 --- a/app/src/main/res/values-jv/strings.xml +++ b/app/src/main/res/values-jv/strings.xml @@ -40,8 +40,6 @@ Sesirah Wedharan Ora bisa mlebu log - jaringané gagal - Ora bisa mlebu log - tiliki jeneng panganggoné panjenengan - Ora bisa mlebu log - tiliki tembung wadiné panjenengan Kakèhan upaya sing gagal. Jajalana manèh mengko. Ngapunten, panganggo iki wis diblokir ing Commons Panjenengan kudu ngisi kodhe otèntifikasi rong faktoré panjenengan diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml index 2bb1c5191..60320dd9e 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -42,8 +42,6 @@ სათაური აღწერა შესვლა ვერ ხერხდება - ქსელის შეცდომა - შესვლა ვერ ხერხდება - გთხოვთ შეამოწმოთ სახელი - შესვლა ვერ ხერხდება - გთხოვთ შეამოწმოთ პაროლი ძალიან ბევრი წარუმატებელი მცდელობა. გთხოვთ, რამდენიმე წუთში სცადეთ კვლავ. უკაცრავად, ეს მომხმარებელი დაბლოკილია ვიკისაწყობში თქვენ უნდა შეიყვანოთ ორფაქტორიანი ავტორიზაციის კოდი. diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml index 342c615b9..c88a34978 100644 --- a/app/src/main/res/values-kab/strings.xml +++ b/app/src/main/res/values-kab/strings.xml @@ -42,8 +42,6 @@ Azwel Aglam Ur izmir ara ad yeqqen - tuccḍa n uẓeṭṭa - Ur izmir ara ad yeqqen - wali isem-ik n useqdac - Ur izmir ara ad yeqqen - wali awal-ik uffir Ddeq n uɛraḍ ur yeddin ara. Ɛreḍ akka di kra n tisdatin Suref-aɣ, aseqdac-agi yewḥel di Commons Yessefk ad d-muddeḍ tangalt n n usesbteb s snat n tarrayin. diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml index c057e04c7..ff4a6ce79 100644 --- a/app/src/main/res/values-km/strings.xml +++ b/app/src/main/res/values-km/strings.xml @@ -35,8 +35,6 @@ ចំណងជើង បរិយាយ មិនអាចកត់ឈ្មោះចូល - បណ្តាញ network បរាជ័យ - មិនអាចកត់ឈ្មោះចូល - សូមពិនិត្យឈ្មោះអ្នកប្រើប្រាស់របស់អ្នក - មិនអាចកត់ឈ្មោះចូល - សូមពិនិត្យលេខសម្ងាត់របស់អ្នក ការព្យាយាមមិនបានសម្រេចមានចំនួនច្រើនដងពេក។ សូមព្យាយាមម្តងទៀតនៅប៉ុន្មាននាទីក្រោយ។ សូមអភ័យទោស អ្នកប្រើប្រាស់រូបនេះត្រូវបានហាមឃាត់នៅ Commons កត់ឈ្មោះចូលបរាជ័យ diff --git a/app/src/main/res/values-ko-rKP/strings.xml b/app/src/main/res/values-ko-rKP/strings.xml index 670479f75..ccbe86100 100644 --- a/app/src/main/res/values-ko-rKP/strings.xml +++ b/app/src/main/res/values-ko-rKP/strings.xml @@ -42,8 +42,6 @@ 제목 설명 가입할수 없습니다 - 망 오유입니다 - 가입할수 없습니다 - 사용자이름을 확인하세요 - 가입할수 없습니다 - 통행암호를 확인하세요 실패한 시도가 너무 많습니다. 몇분후에 다시 시도하세요. 죄송합니다, 이 사용자는 공용에서 차단되였습니다 두인자검증부호를 제공해야 합니다. diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 883665c85..49763dda8 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -58,8 +58,6 @@ 이 파일의 제목을 지정해 주십시오 설명 로그인할 수 없습니다 - 네트워크 오류입니다 - 로그인할 수 없습니다 - 사용자 이름을 확인하세요 - 로그인할 수 없습니다 - 비밀번호를 확인하세요 실패한 시도가 너무 많습니다. 몇 분 후에 다시 시도하세요. 죄송합니다, 이 사용자는 공용에서 차단되었습니다 2요소 인증 코드를 제공해야 합니다. diff --git a/app/src/main/res/values-kum/strings.xml b/app/src/main/res/values-kum/strings.xml index 1630600cd..34db9a395 100644 --- a/app/src/main/res/values-kum/strings.xml +++ b/app/src/main/res/values-kum/strings.xml @@ -38,7 +38,7 @@ Тасвири Такрарламакъ Гери алмакъ - Юклемек + Эндирмек CC0 CC BY-SA 3.0 CC BY-SA 3.0 (Алмания) @@ -47,6 +47,7 @@ CC BY-SA 4.0 CC BY 4.0 CC Zero + Интернетден эндирген суратларынг Юклев уьлгю: Дюр! Категориялар diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml index 6d78e91fe..33e79bd3c 100644 --- a/app/src/main/res/values-ky/strings.xml +++ b/app/src/main/res/values-ky/strings.xml @@ -39,8 +39,6 @@ Аталышы Баяндамасы Кирүүгө болбой жатат - тармакта үзгүлтүк бар - Кирүүгө мүмкүн эмес - сураныч, колдонуучу ысымыңызды текшериңиз - Кирүүгө мүмкүн эмес - сураныч, сыр сөзүңүздү текшериңиз Өтө көп натыйжасыз иш аракет. Суранабыз, бир нече мүнөттөн кийин кайталаңыз Кечириңиз, бул кодонуучу Уикиказынада блокко алынган. Системага кирүүдө катачылык бар! diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index d346b90bd..fa333fa23 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -50,8 +50,6 @@ Titel Beschreiwung Aloggen huet net funktionéiert - Feeler mam Reseau - Login net méiglech - kuckt w.e.g. Äre Benotzernumm no - Login net méiglech - kuckt w.e.g. Äert Passwuert no Ze dacks ouni Succès probéiert. Probéiert w.e.g. an e puer Minutten nach eng Kéier. Pardon, dëse Benotzer ass op Commons gespaart Aloggen huet net funktionéiert diff --git a/app/src/main/res/values-li/strings.xml b/app/src/main/res/values-li/strings.xml index a14add497..f92167522 100644 --- a/app/src/main/res/values-li/strings.xml +++ b/app/src/main/res/values-li/strings.xml @@ -50,8 +50,6 @@ Gaef estebleef \'ne naam veur dit bestandj Besjrieving Kan zich neet aanmelde - netwirkfout - Kan zich neet aanmelde - controleer de gebroekersnaam - Kan zich neet aanmelde - controleer die wachwaord Te väöl mislökde kieëre geperbeerd. Perbeer estebleef oppernuuj euver e paar menuut. Deze gebroeker is geblokkeerd op Commons Doe mós diene twieëfaktorische bevestigingscode opgaeve. diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index c5c0813ab..7fc63d8b9 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -45,8 +45,6 @@ Pavadinimas Aprašymas Negalima prisijungti - tinklo klaida - Negalima prisijungti - prašome patikrinti savo vartotojo vardą - Negalima prisijungti - prašome patikrinti savo slaptažodį Per daug nesėkmingų bandymų. Pabandykite dar kartą po keleto minučių. Atsiprašome, šis vartotojas buvo užblokuotas Commons Prisijungti nepavyko diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index a344fcad2..ee3d94a83 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -49,8 +49,7 @@ Ставете ѝ наслов на податотеката Опис Не можам да Ве најавам — мрежата не работи - Не можам да Ве најавам — проверете си го корисничкото име - Не можам да Ве најавам — проверете си ја лозинката + Не можев да ве најавам. Проверете ги корисничкото име и лозинката. Направени се премногу неуспешни обиди. Обидете се пак за некоја минута. Нажалост, корисникот е блокиран на Ризницата Мора да го укажете вашиот код за двочинителска заверка. diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 632cc0265..c057fb068 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -50,8 +50,6 @@ ഈ പ്രമാണത്തിന് ഒരു തലക്കെട്ട് നൽകുക. വിവരണം പ്രവേശിക്കാനായില്ല - നെറ്റ്‌വർക്ക് പരാജയപ്പെട്ടു - പ്രവേശിക്കാനായില്ല - ദയവായി താങ്കളുടെ ഉപയോക്തൃനാമം പരിശോധിക്കുക - പ്രവേശിക്കാനായില്ല - ദയവായി താങ്കളുടെ രഹസ്യവാക്ക് പരിശോധിക്കുക നിരവധി വിജയകരമല്ലാത്ത ശ്രമങ്ങൾ നടന്നിരിക്കുന്നു. വീണ്ടും ശ്രമിക്കുന്നതിനു മുമ്പ് ഏതാനം മിനിറ്റുകൾ വിശ്രമിക്കുക. ക്ഷമിക്കുക, ഈ ഉപയോക്താവ് കോമൺസിൽ നിന്ന് തടയപ്പെട്ടിരിക്കുകയാണ് താങ്കളുടെ ദ്വി-ഘടക സാധൂകരണ കോഡ് നൽകുക. diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml index b88e021e7..e0a15ad36 100644 --- a/app/src/main/res/values-mr/strings.xml +++ b/app/src/main/res/values-mr/strings.xml @@ -54,8 +54,6 @@ कृपया या फाईलसाठी शीर्षक प्रदान करा वर्णन सनोंद प्रवेश अशक्य - नेटवर्क नाही - सनोंद प्रवेश अशक्य - कृपया आपले सदस्यनाव तपासा - सनोंद प्रवेश अशक्य - कृपया आपला परवलीचा शब्द तपासा अनेक अयशस्वी प्रयत्न.काही मिनीटांनंतर पुन्हा प्रयत्न करा. माफ करा, कॉमन्सवर हा सदस्य प्रतिबंधित आहे आपण आपल्या दोन कारक प्रमाणिकरण कोड प्रदान करणे आवश्यक आहे. diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml index c5c0f18ab..ef244d049 100644 --- a/app/src/main/res/values-ms/strings.xml +++ b/app/src/main/res/values-ms/strings.xml @@ -37,8 +37,6 @@ Tajuk Keterangan Tidak boleh log masuk - kegagalan rangkaian - Tidak dapat log masuk - Sila semak nama pengguna anda - Tidak dapat log masuk - Sila semak kata laluan anda Terlalu banyak cubaan yang tidak berjaya. Sila cuba lagi dalam beberapa minit Maaf, pengguna ini telah disekat di Commons Log masuk gagal diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 9774035ee..d2f622fce 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -49,8 +49,6 @@ Tittel Beskrivelse Innlogging feilet - nettverksproblem - Innlogging feilet - sjekk brukernavnet ditt - Innlogging feilet - sjekk passordet ditt For mange misslykkede forsøk. Vennligst prøv igjen om noen få minutter. Beklager, denne brukeren har blitt blokkert på Commons Du må oppgi tofaktorautentiseringskoden din. diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index db9d8d3fd..512a655eb 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -39,8 +39,6 @@ शीर्षक वर्णन प्रवेश गर्न असमर्थ - जडान खराबी - प्रवेश गर्न असमर्थ - कृपया तपाईंको प्रयोगकर्ता नाम जाँच गर्नुहोस् - प्रवेश गर्न असमर्थ - कृपया आफ्नो पासवर्ड जाँच गर्नुहोस धेरै असफल प्रयासहरू भए । कृपया केही मिनेट पछि पुन: प्रयास गर्नुहोस माफ गर्नुहोस, यो प्रयोगकर्तालाई कमोन्समा ब्लक गरिएको छ प्रवेश सफल हुन सकेन diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index f2688cffa..b6cec0ebd 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -47,8 +47,6 @@ Naam Beschrijving Aanmelden niet mogelijk. Er is een probleem met het netwerk - Aanmelden niet mogelijk. Controleer uw gebruikersnaam - Aanmelden niet mogelijk. Controleer uw wachtwoord U hebt te vaak geprobeerd aan te melden. Probeer het over een aantal minuten opnieuw. Deze gebruiker is helaas geblokkeerd op Wikimedia Commons Aanmelden mislukt diff --git a/app/src/main/res/values-oc/strings.xml b/app/src/main/res/values-oc/strings.xml index 803293a55..2c2d94d70 100644 --- a/app/src/main/res/values-oc/strings.xml +++ b/app/src/main/res/values-oc/strings.xml @@ -43,8 +43,6 @@ Títol Descripcion Impossible de se connectar — pana de ret - Impossible de se connectar — verificatz vòstre nom d’utilizaire - Impossible de se connectar — verificatz vòstre senhal Tròp de temptativas infructuosas. Ensajatz tornarmai dins qualques minutas. O planhèm, aqueste utilizaire es estat blocat dins Commons Error de connexion diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml index c1bc2b73f..a1e08984a 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -35,8 +35,6 @@ ଶିରୋନାମ ବିବରଣୀ ଲଗ ଇନ କରିବାରେ ବିଫଳ - ନେଟୱାର୍କରେ ଅସୁବିଧା - ଲଗ ଇନ କରିବାରେ ବିଫଳ - ଦୟାକରି ନିଜର ସଭ୍ୟ ନାମ ପରଖିନିଅନ୍ତୁ - ଲଗ ଇନ କରିବାରେ ବିଫଳ - ଦୟାକରି ନିଜର ପାସୱାର୍ଡ଼ ପରଖିନିଅନ୍ତୁ ଖୁବ ଅଧିକ ଅସଫଳ ଚେଷ୍ଟା । ଦୟାକରି କେଇ ମିନିଟ ଛାଡ଼ି ଚେଷ୍ଟା କରନ୍ତୁ କ୍ଷମା ଘେନିବେ, ଏହି ସଭ୍ୟଙ୍କୁ କମନ୍ସରେ ଅଟକାଯାଇଛି ଲଗଇନ ହେଲାନାହିଁ diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 6937ab75d..952ad877f 100644 --- a/app/src/main/res/values-pa/strings.xml +++ b/app/src/main/res/values-pa/strings.xml @@ -43,8 +43,6 @@ ਸਿਰਲੇਖ ਵੇਰਵਾ ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਨੈੱਟਵਰਕ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ - ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਆਪਣਾ ਵਰਤੋਂਕਾਰ ਨਾਂ ਚੈੱਕ ਕਰੋ - ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਆਪਣਾ ਪਾਸਵਰਡ ਚੈੱਕ ਕਰੋ ਜੀ ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ। ਅਫ਼ਸੋਸ, ਇਹ ਵਰਤੋਂਕਾਰ ਕਾਮਨਜ਼ ਉੱਤੇ ਬਲਾਕ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ ਦਾਖ਼ਲਾ ਫੇਲ੍ਹ ਹੋਇਆ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index ccc5f374f..98e7212df 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -60,8 +60,6 @@ Tytuł Opis Nie można zalogować - błąd sieci - Nie można zalogować - sprawdź nazwę użytkownika - Nie można zalogować - sprawdź hasło Zbyt wiele nieudanych prób zalogowania. Spróbuj ponownie za kilka minut. Przepraszamy, ten użytkownik został zablokowany na Commons Wprowadź swój kod dla dwuetapowej autoryzacji. diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 8b2bd292c..fd1fe3d22 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -49,8 +49,7 @@ Për piasì, ch\'a-j buta \'n tìtol a s\'archivi Descrission Impossìbil rintré ant ël sistema - la rej a marcia nen - Impossìbil rintré ant ël sistema - për piasì, ch\'a verìfica sò stranòm - Impossìbil rintré ant ël sistema - për piasì, ch\'a contròla soa ciav + Impossìbil rintré ant ël sistema - për piasì ch\'a contròla sò stranòm e soa ciav Tròpi tentativ falì. Për piasì, ch\'a preuva torna da-sì chèiche minute. An dëspias, s\'utent-sì a l\'é stàit blocà ansima a Commons A dev fornì sò còdes d\'autentificassion a doi fator. diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml index 3e2b499e6..2010b9bd5 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -37,8 +37,6 @@ مهرباني وکړئ د دې دوتنې لپاره سرلیک چمتو کړئ څرگندونه د ننوتلو توان نلري - د شبکې ناکامي - د ننوتلو توان نلري - لطفاً خپل کارن نوم وګورئ - د ننوتلو توان نلري - لطفاً خپل پټنوم وګورئ ډیری ناکامه هڅې. لطفا څو دقیقې وروسته بیا هڅه وکړئ. بخښنه غواړو، په دي کارن د کامنز لخوا بنديز ولګول شو غونډال کې ننوتنه نابريالې شوه diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 849c4e6b1..e7b70a35b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -61,8 +61,7 @@ Forneça um título para este arquivo Descrição Erro ao efetuar o login - falha na rede - Erro ao efetuar o login - confira seu nome de usuário - Erro ao efetuar o login - confira sua senha + Não é possível fazer o login - verifique seu nome de usuário e senha Muitas tentativas malsucedidas. Tente de novo daqui alguns minutos. Desculpe, esse usuário foi banido do Commons Você precisa fornecer o seu código de ativação de dois fatores. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index e59582fa0..888a453b9 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -58,8 +58,6 @@ Forneça um título para este ficheiro, por favor Descrição Não foi possível iniciar sessão - falha de rede - Não foi possível iniciar sessão - verifique o seu nome de utilizador(a) - Não foi possível iniciar sessão - verifique a sua palavra-passe Demasiadas tentativas malsucedidas. Por favor, tente de novo dentro de alguns minutos. Desculpe, este utilizador foi bloqueado no Commons Precisa fornecer o seu código de ativação de dois fatores. diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml index d84ce08ab..fde276f7c 100644 --- a/app/src/main/res/values-qq/strings.xml +++ b/app/src/main/res/values-qq/strings.xml @@ -50,8 +50,6 @@ {{Identical|Title}} {{Identical|Description}} Error message shown to user when login can not be completed due to network issues. - Error message shown to user when login can not be completed because the user name is wrong. - Error message shown to user when login can not be completed beause the password is wrong Error message shown to user when login can not be completed because the user has attempted to login too many times in a short period of time, and hence been throttled. Error message shown to user when login can not be completed because the user is blocked on Wikimedia Commons {{Identical|Login failed}} diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index b87abfbbf..52b301fc8 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -44,8 +44,6 @@ Titlu Descriere Autentificare nereușită – defecțiune de rețea - Autentificare nereușită – verificați-vă numele de utilizator - Autentificare nereușită – verificați-vă parola Prea multe încercări nereușite. Încercați din nou peste câteva minute. Ne pare rău, acest utilizator a fost blocat la Commons Trebuie să introduceți tokenul de autentificare. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0d5a0ee3e..747f368d5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -62,8 +62,7 @@ Пожалуйста, укажите название этого файла Описание Не удаётся войти — сбой сети - Не удалось войти — пожалуйста, проверьте своё имя пользователя - Не удалось войти — пожалуйста, проверьте свой пароль + Не удаётся войти — проверьте ваше имя пользователя и пароль Слишком много неудачных попыток. Пожалуйста, попробуйте ещё раз через несколько минут. Извините, но участник с таким именем был заблокирован на Викискладе Вы должны ввести код двухфакторной аутентификации. diff --git a/app/src/main/res/values-sd/strings.xml b/app/src/main/res/values-sd/strings.xml index 3ccc39dd3..07043e1bd 100644 --- a/app/src/main/res/values-sd/strings.xml +++ b/app/src/main/res/values-sd/strings.xml @@ -42,8 +42,6 @@ عنوان تشريح ناقابلِ داخل ٿيڻ - باھمڄار ناڪامي - ناقابلِ داخل ٿيڻ - براءِ مھرباني پنھنجو واپرائيندڙ-نانءُ چڪاسيو - ناقابل داخل ٿيڻ - براءِ مھرباني پنھنجو ڳجھولفظ چڪاسيو ھيڪانديون ناڪام ڪوششون. براءِ مھرباني ڪجھ منٽن کانپوءِ ٻيھر ڪوشش ڪريو. افسوس، ھي واپرائيندڙ العام تي بندشيل آھي داخل ٿيڻ ناڪام diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml index 4ae0db17c..cb553b304 100644 --- a/app/src/main/res/values-si/strings.xml +++ b/app/src/main/res/values-si/strings.xml @@ -44,8 +44,6 @@ මාතෘකාව විස්තරය පිවිසීමට නොහැකිය-ජාලය ඇනහිටීමක් - පිවිසීමට නොහැකිය-කරුණාකර ඔබගේ පිවිසුම්-නාමය පරික්ෂා කරන්න - පිවිසීමට නොහැකිය-කරුණාකර ඔබගේ මුරපදය පරික්ෂා කරන්න. බොහෝ අසාර්ථක උත්සාහයන් කර ඇත. කරුණාකර මිනිත්තු කිහිපයකට පසුව උත්සාහ කරන්න කණගාටුයි,මෙම පරිශීලක වාරණයට ලක්කර ඇත. ඔබ ඔබගේ ද්විත්ව සහතික කිරීමේ කේතය ඇතුලත් කළ යුතුය. diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 90fac7f16..9ad6857ef 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -51,8 +51,6 @@ Prosím, dajte tomuto súboru názov Opis prihlásenie zlyhalo - zlyhanie siete - Prihlásenie zlyhalo - skontrolujte vaše používateľské meno - Prihlásenie zlyhalo - skontrolujte vaše heslo Príliš veľa neúspešných pokusov. Skúste to znova o niekoľko minút. Ospravedlňujeme sa, tento užívateľ bol na Commons zablokovaný Prihlásenie zlyhalo diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 925b8a5ce..312cccb62 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -57,8 +57,6 @@ Унесите наслов за ову датотеку Опис Неуспешно пријављивање – грешка на мрежи - Неуспешно пријављивање – проверите Ваше корисничко име - Неуспешно пријављивање – проверите Вашу лозинку Превише неуспешних покушаја. Пробајте поново за неколико минута. Нажалост, овај корисник је блокиран на Остави Морате унети Ваш двофакторски код за аутентификацију. diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index b2604eefd..3fff0ed27 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -43,8 +43,6 @@ Judul Pedaran Teu bisa login - gangguan jaringan - Teu bisa login - pariksa sandiasma - Teu bisa login - pariksa kecap sandi Loba teuing nu gagalna. Mangga cobian sababaraha menit deui mah Punten, ieu kontributor geus diblokir di Commons Anjeun kudu nyayagakeun kodeu oténtikasi dua faktor. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 1d2fc868e..8c9e8a478 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -55,8 +55,7 @@ Var god ange en titel för denna fil Beskrivning Det gick inte att logga in - nätverksfel - Det gick inte att logga in - var god kontrollera ditt användarnamn - Det gick inte att logga in - var god kontrollera ditt lösenord + Kunde inte logga in - kontrollera ditt användarnamn och lösenord För många misslyckade försök. Var god försök igen om några minuter. Tyvärr, denna användare har blockerats på Commons Du måste ange din tvåstegsverifieringskod. diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml index b2ad478b0..59c919d35 100644 --- a/app/src/main/res/values-te/strings.xml +++ b/app/src/main/res/values-te/strings.xml @@ -38,8 +38,6 @@ శీర్షిక వివరణ లాగిన్ చెయ్యలేకపోయాం - నెట్‍వర్కు విఫలం - లాగిన్ చెయ్యలేకపోయాం - మీ వాడుకరిపేరును సరిచూసుకోండి - లాగిన్ చెయ్యలేకపోయాం - మీ సంకేతపదాన్ని సరిచూసుకోండి మరీ ఎక్కువ విఫల యత్నాలు చేసారు. కొద్ది నిముషాలాగి ప్రయత్నించండి ఈ వాడుకరి కామన్స్ లో నిరోధించబడ్డారు, సారీ. లాగిన్ విఫలమైంది diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 29b29d804..933f0c2d4 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -48,8 +48,6 @@ กรุณาระบุชืิ่อเรื่องของไฟล์นี้ คำอธิบาย ไม่สามารถเข้าสู่ระบบได้ - ความล้มเหลวของเครือข่าย - ไม่สามารถเข้าสู่ระบบได้ - กรุณาตรวจสอบชื่อผู้ใช้ของคุณ - ไม่สามารถเข้าสู่ระบบได้ - กรุณาตรวจสอบรหัสผ่านของคุณ จำนวนครั้งที่พยายามไม่สำเร็จมากเกินไป กรุณาลองอีกครั้งในอีกสักครู่ ขออภัย ผู้ใช้นี้ถูกบล็อกบนคอมมอนส์อยู่ คุณต้องระบุโค้ดการตรวจสอบความถูกต้องสองปัจจัยของคุณ diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index c9dd60b41..784458cfa 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -57,8 +57,6 @@ Lütfen bu dosya için bir başlık ekleyin Açıklama Oturum açılamıyor - ağ hatası - Oturum açılamıyor - lütfen kullanıcı adınızı kontrol edin - Oturum açılamıyor - lütfen parolanızı kontrol edin Çok sayıda başarısız girişimde bulundunuz. Birkaç dakika sonra tekrar deneyin. Üzgünüz, bu kullanıcı Commons\'ta engellendi İki faktörlü kimlik doğrulama kodunu sağlamalısınız. diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 3c180fbf2..4c55cb548 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -39,8 +39,6 @@ ماۋزۇ چۈشەندۈرۈش تىزىملىتىش - تور كاشىلىسى - تىزىملاشقا ئامالسىز - سىزنىڭ ئابونت نامىڭىزنى تەكشۈرۈپ بېقىڭ - تىزىملاشقا ئامالسىز مەخپىي نومۇرىڭىزنى تەكشۈرۈپ بېقىڭ مەغلۇپ بولغان قېتىم سانى بەك كۆپ . نەچچە مىنۇتتىن كېيىن قايتا سىناڭ . كەچۈرۈڭ، بۇ ئابونت ئاللىقاچان ئورتاق بەھرىمەن بولىدىغان بايلىق مەنبەسى دائىرىلىك سىز چوقۇم سىزنىڭ قوش ئامىل تەكشۈرۈش كودىنى تاپشۇرىسىز . diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 0e83e6be0..54c1f86c5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -58,8 +58,6 @@ Будь ласка, вкажіть назву цього файлу Опис Неможливо увійти — збій у мережі - Неможливо увійти — будь ласка, перевірте своє ім\'я користувача - Неможливо увійти — будь ласка, перевірте свій пароль Надто багато невдалих спроб. Будь ласка, спробуйте знову через кілька хвилин. Вибачте, цього користувача було заблоковано на Вікісховищі Ви повинні надати код двофакторної автентифікації. @@ -150,7 +148,7 @@ Корисні об\'єкти (велосипеди, залізничні станції) Відомі люди (ваш мер, спортсмен-олімпієць, якого ви зустріли) Будь ласка, НЕ завантажуйте: - - Селфі або фото своїх друзів \n- Зображення, які Ви завантажили з інтернету \n- Скріншоти патентованих програм + - Селфі або фото своїх друзів \n- Зображення, які Ви завантажили з інтернету \n- Знімки екрану пропрієтарних програм Селфі чи фото ваших друзів Зображення, які ви завантажили з інтернету Знімки екрану пропрієтарних програм @@ -162,7 +160,7 @@ Надсилайте Ваші зображення. Допоможіть оживити статті Вікіпедії! Зображення у Вікіпедії надходять з Вікісховища. Ваші зображення допомагають освіті людей у всьому світі. - Уникайте захищених авторським правом матеріалів, знайдених в Інтернеті, а також зображень плакатів, обкладинок книг і т. п. + Уникайте захищених авторським правом матеріалів, знайдених в Інтернеті, а також зображень плакатів, обкладинок книг, тощо. Ви це зрозуміли? Так! diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml index 4fdd67514..2620412d6 100644 --- a/app/src/main/res/values-ur/strings.xml +++ b/app/src/main/res/values-ur/strings.xml @@ -45,8 +45,6 @@ عنوان وضاحت لاگ ان ہونے میں ناکام - نیٹ ورک ناکامی - لاگ ان ہونے میں ناکام - براہ مہربانی اپنا صارف نام کی جانچ کریں - لاگ ان ہونے میں ناکام - براہ مہربانی - اپنے پاس ورڈ کی جانچ کریں بے شمار ناکام کوششیں کچھ منٹوں میں دوبارہ کوشش کریں۔ معذرت، یہ صارف کومنز پر بلاک کردیا گیا ہے آپ کو اپنے دو عامل کے تصدیق کوڈ فراہم کرنا چاہیے۔ diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 4cff15064..5b7fd430c 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -1,16 +1,22 @@ + Phản hồi + Vị trí Commons + Thiết lập Tên người dùng Mật khẩu + Đăng nhập vào tài khoản Commons Beta của bạn Đăng nhập + Quên mật khẩu? Mở tài khoản Đang đăng nhập Vui lòng chờ… @@ -26,7 +32,10 @@ Đang hoàn thành việc tải lên tập tin %1$s Tải lên tập tin %1$s thất bại Chạm để xem - %d tập tin đang được tải lên + + %1$d tập tin đang được tải lên + %1$d tập tin đang được tải lên + Tập tin Tôi đã Tải lên Gần đây Đang chờ Thất bại @@ -39,10 +48,9 @@ Chia sẻ Mở trong Trình duyệt Tên + Xin hãy đặt tiêu đề cho tập tin này Miêu tả Không thể đăng nhập – có lỗi mạng - Không thể đăng nhập – xin vui lòng kiểm tra tên người dùng - Không thể đăng nhập – xin vui lòng kiểm tra mật khẩu Đã đăng nhập thất bại quá nhiều lần. Xin vui lòng thử lại trong vòng vài phút. Rất tiếc, người dùng này đã bị cấm tại Commons Bạn phải cung cấp mã xác thực dùng hai nhân tố. @@ -54,25 +62,31 @@ Tìm thể loại Lưu Làm mới + Danh sách Chức năng GPS đang tắt trên thiết bị của bạn. Bạn có muốn bật nó lên? Bật GPS Chưa có tập tin tải lên - + \@string/contributions_subtitle_zero - %1$d tập tin tải lên + %1$d tập tin đã tải lên + %1$d tập tin đã tải lên Đang bắt đầu tải lên %1$d tập tin - %1$d tập tin tải lên + + %1$d tập tin đã tải lên + %1$d tập tin đã tải lên + Không tìm thấy thể loại khớp với %1$s - Xếp các hình ảnh vào thể loại để cho chúng dễ tìm kiếm hơn trên Wikimedia Commons.\n\nHãy bắt đầu nhập tên thể loại để tìm kiếm.\nChạm vào thông điệp này (hoặc bấm Quay lại) để bỏ qua bước này. + Xếp các hình ảnh vào thể loại để giúp chúng dễ tìm kiếm hơn trên Wikimedia Commons.\nHãy bắt đầu nhập để thêm thể loại. Thể loại Cài đặt Mở tài khoản + Hình ảnh chọn lọc Giới thiệu - Phần mềm mã nguồn mở được phát hành theo <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Giấy phép Apache v2</a>. %1$s và biểu trưng của nó là nhãn hiệu của Quỹ Wikimedia và được sử dụng do Quỹ Wikimedia cho phép. Chúng tôi không được Quỹ Wikimedia ủng hộ hoặc nhận làm chi nhánh. - <a href=\"https://github.com/commons-app/apps-android-commons\">Mã nguồn</a> và <a href=\"https://commons-app.github.io/\">trang chủ</a> tại GitHub. <a href=\"https://github.com/commons-app/apps-android-commons/issues\">Tạo vấn đề GitHub mới</a> để báo cáo lỗi hoặc gợi ý thay đổi. - - <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Công trạng</a> + Ứng dụng Wikimedia Commons là ứng dụng mã nguồn mở được sáng tạo và quản lý bởi các tình nguyện viên và những người được tin tưởng của cộng đồng Wikipedia. Wikimedia Foundation không tham gia vào sự tạo lập, phát triển cũng như quản lý của ứng dụng. + Tạo một <a href=\"https://github.com/commons-app/apps-android-commons/issues\">thảo luận (issue) mới trên GitHub</a> để báo cáo lỗi cũng như đưa ra các ý tưởng. + <u>Chính sách riêng tư</u> + <u>Công trạng</u> Giới thiệu Gửi Phản hồi (qua Thư điện tử) Không có chương trình thư điện tử nào được cài đặt @@ -84,7 +98,7 @@ Hình này sẽ được phát hành theo giấy phép %1$s Với việc đăng hình này, tôi tuyên bố rằng nó là tác phẩm của chính mình, rằng nó không chứa nội dung có bản quyền hoặc ảnh tự chụp, và về mặt khác thì hoàn toàn tuân theo <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines?uselang=vi\">các quy định Wikimedia Commons</a>. Tải về - Giấy phép + Giấy phép mặc định Sử dụng tiêu đề/miêu tả trước Lấy vị trí hiện tại tự động Định vị hiện tại để nhận gợi ý thể loại trong trường hợp hình ảnh chưa được gắn thẻ địa lý diff --git a/app/src/main/res/values-xmf/strings.xml b/app/src/main/res/values-xmf/strings.xml index b9689f9e2..d9c0b8f9d 100644 --- a/app/src/main/res/values-xmf/strings.xml +++ b/app/src/main/res/values-xmf/strings.xml @@ -41,8 +41,6 @@ დუდჯოხო ეჭარუა მიშულაქ ვემიხუჯინუ - რშვილიშ ჩილათა - მიშულაქ ვემიხუჯინუ - ქორთხინთ გეგნაჯინით ჯოხოს - მიშულაქ ვემიხუჯინუ - ქორთხინთ გეგნაჯინით პაროლს ძალამ მიარე უმწუძინუ ცადება. ქორთხინ, მუხირენ წუთშა ხოლო ქოცადით. მორდება, თე მახვარებუ ბლოკირი რე ვიკიოწკარუეს თქვა გემშიონათ ოკო ჟირფაქტორიანი ავტორიზაციაშ კოდი. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 4581d45ce..8b794d1c5 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -57,8 +57,7 @@ 請提供此檔案的標題 說明 無法登入-網路故障 - 無法登入-請檢查您的使用者名稱 - 無法登入-請檢查您的密碼 + 無法登入 - 請檢查您的使用者名稱與密碼 失敗次數過多。請於幾分鐘後重試。 很抱歉,該使用者已被維基共享資源封禁 必須提供您的雙重因素身分核對代碼。 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 2079eb925..ab638cb87 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -55,8 +55,6 @@ 请提供此文件的标题 说明 无法登录 - 网络故障 - 无法登录 - 请检查您的用户名 - 无法登录 - 请检查您的密码 失败次数过多。请在几分钟后重试。 对不起,该用户已经被共享资源封禁 您必须提供您的双因素验证代码。 From 99745db13a821a89fa12a8699b916c9ca57413ae Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 19:45:19 +1000 Subject: [PATCH 40/94] Remove comments --- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 1e33bcddf..464eaf2f3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -109,7 +109,7 @@ public class ShareActivity @BindView(R.id.backgroundImage) SimpleDraweeView backgroundImageView; @BindView(R.id.media_map) - FloatingActionButton mapsFragment; //Lets stick to camelCase + FloatingActionButton mapsFragment; @BindView(R.id.media_upload_zoom_in) FloatingActionButton zoomInButton; @BindView(R.id.media_upload_zoom_out) @@ -123,7 +123,7 @@ public class ShareActivity private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; - + private String source; private String mimeType; From 5f140a645325778d27bb83a7628bf656caef86dc Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 19:49:02 +1000 Subject: [PATCH 41/94] Remove initViewsAndListeners now that ButterKnife has been used --- .../nrw/commons/upload/ShareActivity.java | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 464eaf2f3..f583f58d7 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -283,7 +283,6 @@ public class ShareActivity .build()); receiveIntent(); - initViewsAndListeners(); if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); @@ -348,49 +347,6 @@ public class ShareActivity } } - /** - * Initialize views and setup listeners here for FAB to prevent cluttering onCreate - */ - private void initViewsAndListeners() { - mainFab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if(!isFABOpen) { - showFABMenu(); - } else { - closeFABMenu(); - } - } - }); - - try { - zoomInButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - zoomImageFromThumb(backgroundImageView, mediaUri); - } - }); - } catch (Exception e) { - Timber.e(e); - } - - mapsFragment.setVisibility(View.VISIBLE); - if( imageObj == null || imageObj.imageCoordsExists){ - mapsFragment.setVisibility(View.INVISIBLE); - } - mapsFragment.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if( imageObj != null && imageObj.imageCoordsExists) { - Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); - Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); - mapIntent.setPackage("com.google.android.apps.maps"); - startActivity(mapIntent); - } - } - }); - } - /** * Function to display the zoom and map FAB */ From d1981494ad631a6d5b1cb6b12ac39111568dc1eb Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 19:58:55 +1000 Subject: [PATCH 42/94] Refactor checkIfFileExists() --- .../free/nrw/commons/upload/ShareActivity.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index f583f58d7..56401185b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -36,7 +36,6 @@ import android.view.MenuItem; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; -import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; @@ -308,7 +307,8 @@ public class ShareActivity new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERM_ON_CREATE_LOCATION); } - performPreUploadProcessingOfFile(); + checkIfFileExists(); + getFileMetadata(locationPermitted); SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); @@ -412,7 +412,7 @@ public class ShareActivity case REQUEST_PERM_ON_CREATE_LOCATION: { if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { locationPermitted = true; - performPreUploadProcessingOfFile(); + checkIfFileExists(); } return; } @@ -423,7 +423,7 @@ public class ShareActivity if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //It is OK to call this at both (1) and (4) because if perm had been granted at //snackbar, user should not be prompted at submit button - performPreUploadProcessingOfFile(); + checkIfFileExists(); //Uploading only begins if storage permission granted from arrow icon uploadBegins(); @@ -433,7 +433,10 @@ public class ShareActivity } } - private void performPreUploadProcessingOfFile() { + /** + * Check if file user wants to upload already exists on Commons + */ + private void checkIfFileExists() { if (!useNewPermissions || storagePermitted) { if (!duplicateCheckPassed) { //Test SHA1 of image to see if it matches SHA1 of a file on Commons @@ -450,19 +453,16 @@ public class ShareActivity //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means //we are processing images that are already on server???... - if (duplicateCheckPassed) { //image can be uploaded, so now check if its a useless picture or not performUnwantedPictureDetectionProcess(); } - },mwApi); fileAsyncTask.execute(); } catch (IOException e) { Timber.d(e, "IO Exception: "); } } - getFileMetadata(locationPermitted); } else { Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", useNewPermissions, storagePermitted, locationPermitted); From 56eed67ac4887142486ccaa48c1773c91ac1f223 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 20:00:54 +1000 Subject: [PATCH 43/94] Refactor detectUnwantedPictures() --- .../fr/free/nrw/commons/upload/ShareActivity.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 56401185b..a3f017fe2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -455,7 +455,7 @@ public class ShareActivity //we are processing images that are already on server???... if (duplicateCheckPassed) { //image can be uploaded, so now check if its a useless picture or not - performUnwantedPictureDetectionProcess(); + detectUnwantedPictures(); } },mwApi); fileAsyncTask.execute(); @@ -469,12 +469,13 @@ public class ShareActivity } } - private void performUnwantedPictureDetectionProcess() { + /** + * Calls the async task that detects if image is fuzzy, too dark, etc + */ + private void detectUnwantedPictures() { String imageMediaFilePath = FileUtils.getPath(this,mediaUri); DetectUnwantedPicturesAsync detectUnwantedPicturesAsync - = new DetectUnwantedPicturesAsync(new WeakReference(this) - , imageMediaFilePath); - + = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); detectUnwantedPicturesAsync.execute(); } From 3e3956936b9f4090e1bf7ca50ee7f55d4004fe27 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 20:26:07 +1000 Subject: [PATCH 44/94] Simplify formatting --- .../nrw/commons/upload/ShareActivity.java | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index a3f017fe2..dd6631fa2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -460,7 +460,7 @@ public class ShareActivity },mwApi); fileAsyncTask.execute(); } catch (IOException e) { - Timber.d(e, "IO Exception: "); + Timber.e(e, "IO Exception: "); } } } else { @@ -479,8 +479,8 @@ public class ShareActivity detectUnwantedPicturesAsync.execute(); } - /* - * to display permission snackbar in share activity + /** + * Displays Snackbar to ask for location permissions */ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, @@ -502,23 +502,18 @@ public class ShareActivity // TODO: there might be a more proper solution than this String copyPath = null; try { - ParcelFileDescriptor descriptor - = getContentResolver().openFileDescriptor(mediaUri, "r"); + ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); if (descriptor != null) { boolean useExtStorage = prefs.getBoolean("useExternalStorage", true); if (useExtStorage) { - copyPath = Environment.getExternalStorageDirectory().toString() - + "/CommonsApp/" + new Date().getTime() + ".jpg"; + copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg"; File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); newFile.mkdir(); - FileUtils.copy( - descriptor.getFileDescriptor(), - copyPath); + FileUtils.copy(descriptor.getFileDescriptor(), copyPath); Timber.d("Filepath (copied): %s", copyPath); return copyPath; } - copyPath = getApplicationContext().getCacheDir().getAbsolutePath() - + "/" + new Date().getTime() + ".jpg"; + copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; FileUtils.copy( descriptor.getFileDescriptor(), copyPath); @@ -622,9 +617,7 @@ public class ShareActivity newFragment.show(fragmentManager, "dialog"); break; } - } - } } haveCheckedForOtherImages = true; //Finished checking for other images From c97b708b0f5ca399b6c0ee88f0ff7c9491df3bab Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 22:46:50 +1000 Subject: [PATCH 45/94] Transfer createCopyPath to FileUtils as a static method --- .../fr/free/nrw/commons/upload/FileUtils.java | 18 ++++++++++++++++++ .../free/nrw/commons/upload/ShareActivity.java | 18 +++++++----------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index 612b86458..b5be2b664 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -26,6 +26,24 @@ import java.util.Date; import timber.log.Timber; public class FileUtils { + /** + * In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead. + * @return path of copy + */ + @Nullable + static String createCopyPath(ParcelFileDescriptor descriptor) { + try { + String copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg"; + File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); + newFile.mkdir(); + FileUtils.copy(descriptor.getFileDescriptor(), copyPath); + Timber.d("Filepath (copied): %s", copyPath); + return copyPath; + } catch (IOException e) { + Timber.e(e); + return null; + } + } /** * Get a file path from a Uri. This will get the the path for Storage Access diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index dd6631fa2..3f6f1392d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -492,31 +492,27 @@ public class ShareActivity return snackbar; } + /** + * Gets file path from media URI. + * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead. + * @return file path of media + */ @Nullable private String getPathOfMediaOrCopy() { String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); Timber.d("Filepath: " + filePath); if (filePath == null) { - // in older devices getPath() may fail depending on the source URI - // creating and using a copy of the file seems to work instead. - // TODO: there might be a more proper solution than this String copyPath = null; try { ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); if (descriptor != null) { boolean useExtStorage = prefs.getBoolean("useExternalStorage", true); if (useExtStorage) { - copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg"; - File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); - newFile.mkdir(); - FileUtils.copy(descriptor.getFileDescriptor(), copyPath); - Timber.d("Filepath (copied): %s", copyPath); + copyPath = FileUtils.createCopyPath(descriptor); return copyPath; } copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; - FileUtils.copy( - descriptor.getFileDescriptor(), - copyPath); + FileUtils.copy(descriptor.getFileDescriptor(), copyPath); Timber.d("Filepath (copied): %s", copyPath); return copyPath; } From 41673c0067e4577344946b518c1ed133ac23302c Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 22:48:47 +1000 Subject: [PATCH 46/94] Unify FileUtils.java, misleading to have 2 of it in different packages --- .../free/nrw/commons/CommonsApplication.java | 2 - .../free/nrw/commons/nearby/NearbyPlaces.java | 1 - .../commons/settings/SettingsFragment.java | 6 -- .../fr/free/nrw/commons/upload/FileUtils.java | 81 +++++++++++++++++++ 4 files changed, 81 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 57cb5fad1..276047aaa 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -1,6 +1,5 @@ package fr.free.nrw.commons; -import android.app.Application; import android.content.Context; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; @@ -27,7 +26,6 @@ import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.modifications.ModifierSequenceDao; -import fr.free.nrw.commons.utils.FileUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index d05d81251..0a3ac4eb8 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -17,7 +17,6 @@ import java.util.regex.Pattern; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.utils.FileUtils; import timber.log.Timber; public class NearbyPlaces { 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 6dd6056f7..d4a26727c 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 @@ -3,13 +3,10 @@ package fr.free.nrw.commons.settings; import android.Manifest; import android.app.AlertDialog; import android.content.ActivityNotFoundException; -import android.content.ComponentName; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -24,8 +21,6 @@ import android.support.v4.content.FileProvider; import android.widget.Toast; import java.io.File; -import java.util.ArrayList; -import java.util.List; import javax.inject.Inject; import javax.inject.Named; @@ -35,7 +30,6 @@ import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.ApplicationlessInjection; -import fr.free.nrw.commons.utils.FileUtils; public class SettingsFragment extends PreferenceFragment { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index b5be2b664..e17192145 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -15,11 +15,16 @@ import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import java.io.BufferedReader; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.nio.channels.FileChannel; import java.util.Date; @@ -253,4 +258,80 @@ public class FileUtils { copy(new FileInputStream(source), new FileOutputStream(destination)); } + + /** + * Read and return the content of a resource file as string. + * @param fileName asset file's path (e.g. "/queries/nearby_query.rq") + * @return the content of the file + */ + public static String readFromResource(String fileName) throws IOException { + StringBuilder buffer = new StringBuilder(); + BufferedReader reader = null; + try { + InputStream inputStream = FileUtils.class.getResourceAsStream(fileName); + if (inputStream == null) { + throw new FileNotFoundException(fileName); + } + reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line).append("\n"); + } + } finally { + if (reader != null) { + reader.close(); + } + } + return buffer.toString(); + } + + /** + * Deletes files. + * @param file context + */ + public static boolean deleteFile(File file) { + boolean deletedAll = true; + if (file != null) { + if (file.isDirectory()) { + String[] children = file.list(); + for (String child : children) { + deletedAll = deleteFile(new File(file, child)) && deletedAll; + } + } else { + deletedAll = file.delete(); + } + } + + return deletedAll; + } + + public static File createAndGetAppLogsFile(String logs) { + try { + File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); + if (!commonsAppDirectory.exists()) { + commonsAppDirectory.mkdir(); + } + + File logsFile = new File(commonsAppDirectory,"logs.txt"); + if (logsFile.exists()) { + //old logs file is useless + logsFile.delete(); + } + + logsFile.createNewFile(); + + FileOutputStream outputStream = new FileOutputStream(logsFile); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); + outputStreamWriter.append(logs); + outputStreamWriter.close(); + outputStream.flush(); + outputStream.close(); + + return logsFile; + } catch (IOException ioe) { + Timber.e(ioe); + return null; + } + } + } \ No newline at end of file From 5207d8f59a0fc116a18e642242432db609c353e2 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 22:49:07 +1000 Subject: [PATCH 47/94] Remove unused FileUtils after unifying --- .../fr/free/nrw/commons/utils/FileUtils.java | 92 ------------------- 1 file changed, 92 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java deleted file mode 100644 index d56a7b608..000000000 --- a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java +++ /dev/null @@ -1,92 +0,0 @@ -package fr.free.nrw.commons.utils; - -import android.os.Environment; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; - -import timber.log.Timber; - -public class FileUtils { - /** - * Read and return the content of a resource file as string. - * - * @param fileName asset file's path (e.g. "/queries/nearby_query.rq") - * @return the content of the file - */ - public static String readFromResource(String fileName) throws IOException { - StringBuilder buffer = new StringBuilder(); - BufferedReader reader = null; - try { - InputStream inputStream = FileUtils.class.getResourceAsStream(fileName); - if (inputStream == null) { - throw new FileNotFoundException(fileName); - } - reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - String line; - while ((line = reader.readLine()) != null) { - buffer.append(line).append("\n"); - } - } finally { - if (reader != null) { - reader.close(); - } - } - return buffer.toString(); - } - - /** - * Deletes files. - * @param file context - */ - public static boolean deleteFile(File file) { - boolean deletedAll = true; - if (file != null) { - if (file.isDirectory()) { - String[] children = file.list(); - for (String child : children) { - deletedAll = deleteFile(new File(file, child)) && deletedAll; - } - } else { - deletedAll = file.delete(); - } - } - - return deletedAll; - } - - public static File createAndGetAppLogsFile(String logs) { - try { - File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); - if (!commonsAppDirectory.exists()) { - commonsAppDirectory.mkdir(); - } - - File logsFile = new File(commonsAppDirectory,"logs.txt"); - if (logsFile.exists()) { - //old logs file is useless - logsFile.delete(); - } - - logsFile.createNewFile(); - - FileOutputStream outputStream = new FileOutputStream(logsFile); - OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); - outputStreamWriter.append(logs); - outputStreamWriter.close(); - outputStream.flush(); - outputStream.close(); - - return logsFile; - } catch (IOException ioe) { - Timber.e(ioe); - return null; - } - } -} From 104cb5f1d70e9e38862ba0d713356453d8141a14 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 22:50:22 +1000 Subject: [PATCH 48/94] Change references to upload.FileUtils --- app/src/main/java/fr/free/nrw/commons/CommonsApplication.java | 1 + app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java | 1 + .../main/java/fr/free/nrw/commons/settings/SettingsFragment.java | 1 + 3 files changed, 3 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 276047aaa..61eecee00 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -26,6 +26,7 @@ import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.di.ApplicationlessInjection; import fr.free.nrw.commons.modifications.ModifierSequenceDao; +import fr.free.nrw.commons.upload.FileUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index 0a3ac4eb8..c8d20f753 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -17,6 +17,7 @@ import java.util.regex.Pattern; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.location.LatLng; +import fr.free.nrw.commons.upload.FileUtils; import timber.log.Timber; public class NearbyPlaces { 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 d4a26727c..d35170adf 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 @@ -30,6 +30,7 @@ import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.upload.FileUtils; public class SettingsFragment extends PreferenceFragment { From 68b5e08eb3bc5e600e59ef5a46ebaed9abaf640a Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 22:55:22 +1000 Subject: [PATCH 49/94] Tidy up getFileCoordinates() --- .../nrw/commons/upload/ShareActivity.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 3f6f1392d..a27a60147 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -196,7 +196,7 @@ public class ShareActivity * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController */ private void uploadBegins() { - getFileMetadata(locationPermitted); + getFileCoordinates(locationPermitted); Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG); startingToast.show(); @@ -308,7 +308,7 @@ public class ShareActivity REQUEST_PERM_ON_CREATE_LOCATION); } checkIfFileExists(); - getFileMetadata(locationPermitted); + getFileCoordinates(locationPermitted); SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); @@ -526,15 +526,13 @@ public class ShareActivity /** * Gets coordinates for category suggestions, either from EXIF data or user location - * * @param gpsEnabled if true use GPS */ - private void getFileMetadata(boolean gpsEnabled) { + private void getFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { if (imageObj == null) { - ParcelFileDescriptor descriptor - = getContentResolver().openFileDescriptor(mediaUri, "r"); + ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (descriptor != null) { imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs); @@ -548,18 +546,13 @@ public class ShareActivity } if (imageObj != null) { - // Gets image coords from exif data or user location decimalCoords = imageObj.getCoords(gpsEnabled); - if(decimalCoords==null || !imageObj.imageCoordsExists){ -// Check if the location is from GPS or EXIF -// Find other photos taken around the same time which has gps coordinates - Timber.d("EXIF:false"); - Timber.d("EXIF call"+(imageObj==tempImageObj)); + if (decimalCoords == null || !imageObj.imageCoordsExists){ + //Find other photos taken around the same time which has gps coordinates if(!haveCheckedForOtherImages) findOtherImages(gpsEnabled);// Do not do repeat the process } else { -// As the selected image has GPS data in EXIF go ahead with the same. useImageCoords(); } } From 5e0e01b035b82fa452ce74a6f9779fac953f6cd1 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 23:03:46 +1000 Subject: [PATCH 50/94] Make bulk code transfers to FileProcessor and FileUtils --- .../nrw/commons/upload/FileProcessor.java | 234 +++++++++++++++++ .../fr/free/nrw/commons/upload/FileUtils.java | 43 ++++ .../nrw/commons/upload/ShareActivity.java | 239 ------------------ 3 files changed, 277 insertions(+), 239 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java 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 new file mode 100644 index 000000000..b349eab39 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java @@ -0,0 +1,234 @@ +package fr.free.nrw.commons.upload; + +import android.app.Activity; +import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.util.Date; +import java.util.List; + +import timber.log.Timber; + +import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; +import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; + +public class FileProcessor { + + + /** + * Check if file user wants to upload already exists on Commons + */ + private void checkIfFileExists() { + if (!useNewPermissions || storagePermitted) { + if (!duplicateCheckPassed) { + //Test SHA1 of image to see if it matches SHA1 of a file on Commons + try { + InputStream inputStream = getContentResolver().openInputStream(mediaUri); + Timber.d("Input stream created from %s", mediaUri.toString()); + String fileSHA1 = getSHA1(inputStream); + Timber.d("File SHA1 is: %s", fileSHA1); + + ExistingFileAsync fileAsyncTask = + new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { + Timber.d("%s duplicate check: %s", mediaUri.toString(), result); + duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); + + //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means + //we are processing images that are already on server???... + if (duplicateCheckPassed) { + //image can be uploaded, so now check if its a useless picture or not + detectUnwantedPictures(); + } + },mwApi); + fileAsyncTask.execute(); + } catch (IOException e) { + Timber.e(e, "IO Exception: "); + } + } + } else { + Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", + useNewPermissions, storagePermitted, locationPermitted); + } + } + + /** + * Calls the async task that detects if image is fuzzy, too dark, etc + */ + private void detectUnwantedPictures() { + String imageMediaFilePath = FileUtils.getPath(this,mediaUri); + DetectUnwantedPicturesAsync detectUnwantedPicturesAsync + = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); + detectUnwantedPicturesAsync.execute(); + } + + + /** + * Gets file path from media URI. + * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead. + * @return file path of media + */ + @Nullable + private String getPathOfMediaOrCopy() { + String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); + Timber.d("Filepath: " + filePath); + if (filePath == null) { + String copyPath = null; + try { + ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); + if (descriptor != null) { + boolean useExtStorage = prefs.getBoolean("useExternalStorage", true); + if (useExtStorage) { + copyPath = FileUtils.createCopyPath(descriptor); + return copyPath; + } + copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; + FileUtils.copy(descriptor.getFileDescriptor(), copyPath); + Timber.d("Filepath (copied): %s", copyPath); + return copyPath; + } + } catch (IOException e) { + Timber.w(e, "Error in file " + copyPath); + return null; + } + } + return filePath; + } + + /** + * Gets coordinates for category suggestions, either from EXIF data or user location + * @param gpsEnabled if true use GPS + */ + private void getFileCoordinates(boolean gpsEnabled) { + Timber.d("Calling GPSExtractor"); + try { + if (imageObj == null) { + ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (descriptor != null) { + imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs); + } + } else { + String filePath = getPathOfMediaOrCopy(); + if (filePath != null) { + imageObj = new GPSExtractor(filePath, this, prefs); + } + } + } + + if (imageObj != null) { + decimalCoords = imageObj.getCoords(gpsEnabled); + if (decimalCoords == null || !imageObj.imageCoordsExists){ + //Find other photos taken around the same time which has gps coordinates + if(!haveCheckedForOtherImages) + findOtherImages(gpsEnabled);// Do not do repeat the process + } + else { + useImageCoords(); + } + } + } catch (FileNotFoundException e) { + Timber.w("File not found: " + mediaUri, e); + } + } + + private void findOtherImages(boolean gpsEnabled) { + Timber.d("filePath"+getPathOfMediaOrCopy()); + String filePath = getPathOfMediaOrCopy(); + long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created + File folder = new File(filePath.substring(0,filePath.lastIndexOf('/'))); + File[] files = folder.listFiles(); + Timber.d("folderTime Number:"+files.length); + + for(File file : files){ + if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){ + //Make sure the photos were taken within 20seconds + Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation); + tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos + ParcelFileDescriptor descriptor + = null; + try { + descriptor = getContentResolver().openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (descriptor != null) { + tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(),this, prefs); + } + } else { + if (filePath != null) { + tempImageObj = new GPSExtractor(file.getAbsolutePath(), this, prefs); + } + } + + if(tempImageObj!=null){ + Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled)); + if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){ +// Current image has gps coordinates and it's not current gps locaiton + Timber.d("This fild has image coords:"+ file.getAbsolutePath()); +// Create a dialog fragment for the suggestion + FragmentManager fragmentManager = getSupportFragmentManager(); + SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); + Bundle args = new Bundle(); + args.putString("originalImagePath",filePath); + args.putString("possibleImagePath",file.getAbsolutePath()); + newFragment.setArguments(args); + newFragment.show(fragmentManager, "dialog"); + break; + } + } + } + } + haveCheckedForOtherImages = true; //Finished checking for other images + return; + } + + + /** + * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. + * Then initiates the calls to MediaWiki API through an instance of MwVolleyApi. + */ + public void useImageCoords() { + if (decimalCoords != null) { + Timber.d("Decimal coords of image: %s", decimalCoords); + Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image:"+(imageObj==tempImageObj)); + + // Only set cache for this point if image has coords + if (imageObj.imageCoordsExists) { + double decLongitude = imageObj.getDecLongitude(); + double decLatitude = imageObj.getDecLatitude(); + cacheController.setQtPoint(decLongitude, decLatitude); + } + + MwVolleyApi apiCall = new MwVolleyApi(this); + + List displayCatList = cacheController.findCategory(); + boolean catListEmpty = displayCatList.isEmpty(); + + // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories + if (catListEmpty) { + cacheFound = false; + apiCall.request(decimalCoords); + Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList); + } else { + cacheFound = true; + Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); + MwVolleyApi.setGpsCat(displayCatList); + } + }else{ + Timber.d("EXIF: no coords"); + } + + } + +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index e17192145..0cd45c189 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -25,12 +25,55 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.math.BigInteger; import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Date; import timber.log.Timber; public class FileUtils { + + /** + * Get SHA1 of file from input stream + */ + static String getSHA1(InputStream is) { + + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA1"); + } catch (NoSuchAlgorithmException e) { + Timber.e(e, "Exception while getting Digest"); + return ""; + } + + byte[] buffer = new byte[8192]; + int read; + try { + while ((read = is.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } + byte[] md5sum = digest.digest(); + BigInteger bigInt = new BigInteger(1, md5sum); + String output = bigInt.toString(16); + // Fill to 40 chars + output = String.format("%40s", output).replace(' ', '0'); + Timber.i("File SHA1: %s", output); + + return output; + } catch (IOException e) { + Timber.e(e, "IO Exception"); + return ""; + } finally { + try { + is.close(); + } catch (IOException e) { + Timber.e(e, "Exception on closing MD5 input stream"); + } + } + } + /** * In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead. * @return path of copy diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index a27a60147..bbc278603 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -433,51 +433,6 @@ public class ShareActivity } } - /** - * Check if file user wants to upload already exists on Commons - */ - private void checkIfFileExists() { - if (!useNewPermissions || storagePermitted) { - if (!duplicateCheckPassed) { - //Test SHA1 of image to see if it matches SHA1 of a file on Commons - try { - InputStream inputStream = getContentResolver().openInputStream(mediaUri); - Timber.d("Input stream created from %s", mediaUri.toString()); - String fileSHA1 = getSHA1(inputStream); - Timber.d("File SHA1 is: %s", fileSHA1); - - ExistingFileAsync fileAsyncTask = - new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { - Timber.d("%s duplicate check: %s", mediaUri.toString(), result); - duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); - - //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means - //we are processing images that are already on server???... - if (duplicateCheckPassed) { - //image can be uploaded, so now check if its a useless picture or not - detectUnwantedPictures(); - } - },mwApi); - fileAsyncTask.execute(); - } catch (IOException e) { - Timber.e(e, "IO Exception: "); - } - } - } else { - Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", - useNewPermissions, storagePermitted, locationPermitted); - } - } - - /** - * Calls the async task that detects if image is fuzzy, too dark, etc - */ - private void detectUnwantedPictures() { - String imageMediaFilePath = FileUtils.getPath(this,mediaUri); - DetectUnwantedPicturesAsync detectUnwantedPicturesAsync - = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); - detectUnwantedPicturesAsync.execute(); - } /** * Displays Snackbar to ask for location permissions @@ -492,126 +447,6 @@ public class ShareActivity return snackbar; } - /** - * Gets file path from media URI. - * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead. - * @return file path of media - */ - @Nullable - private String getPathOfMediaOrCopy() { - String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); - Timber.d("Filepath: " + filePath); - if (filePath == null) { - String copyPath = null; - try { - ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); - if (descriptor != null) { - boolean useExtStorage = prefs.getBoolean("useExternalStorage", true); - if (useExtStorage) { - copyPath = FileUtils.createCopyPath(descriptor); - return copyPath; - } - copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg"; - FileUtils.copy(descriptor.getFileDescriptor(), copyPath); - Timber.d("Filepath (copied): %s", copyPath); - return copyPath; - } - } catch (IOException e) { - Timber.w(e, "Error in file " + copyPath); - return null; - } - } - return filePath; - } - - /** - * Gets coordinates for category suggestions, either from EXIF data or user location - * @param gpsEnabled if true use GPS - */ - private void getFileCoordinates(boolean gpsEnabled) { - Timber.d("Calling GPSExtractor"); - try { - if (imageObj == null) { - ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (descriptor != null) { - imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs); - } - } else { - String filePath = getPathOfMediaOrCopy(); - if (filePath != null) { - imageObj = new GPSExtractor(filePath, this, prefs); - } - } - } - - if (imageObj != null) { - decimalCoords = imageObj.getCoords(gpsEnabled); - if (decimalCoords == null || !imageObj.imageCoordsExists){ - //Find other photos taken around the same time which has gps coordinates - if(!haveCheckedForOtherImages) - findOtherImages(gpsEnabled);// Do not do repeat the process - } - else { - useImageCoords(); - } - } - } catch (FileNotFoundException e) { - Timber.w("File not found: " + mediaUri, e); - } - } - - private void findOtherImages(boolean gpsEnabled) { - Timber.d("filePath"+getPathOfMediaOrCopy()); - String filePath = getPathOfMediaOrCopy(); - long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created - File folder = new File(filePath.substring(0,filePath.lastIndexOf('/'))); - File[] files = folder.listFiles(); - Timber.d("folderTime Number:"+files.length); - - for(File file : files){ - if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){ - //Make sure the photos were taken within 20seconds - Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation); - tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos - ParcelFileDescriptor descriptor - = null; - try { - descriptor = getContentResolver().openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r"); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (descriptor != null) { - tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(),this, prefs); - } - } else { - if (filePath != null) { - tempImageObj = new GPSExtractor(file.getAbsolutePath(), this, prefs); - } - } - - if(tempImageObj!=null){ - Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled)); - if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){ -// Current image has gps coordinates and it's not current gps locaiton - Timber.d("This fild has image coords:"+ file.getAbsolutePath()); -// Create a dialog fragment for the suggestion - FragmentManager fragmentManager = getSupportFragmentManager(); - SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); - Bundle args = new Bundle(); - args.putString("originalImagePath",filePath); - args.putString("possibleImagePath",file.getAbsolutePath()); - newFragment.setArguments(args); - newFragment.show(fragmentManager, "dialog"); - break; - } - } - } - } - haveCheckedForOtherImages = true; //Finished checking for other images - return; - } //I might not be supposed to change it, but still, I saw it @Override @@ -629,43 +464,6 @@ public class ShareActivity } - /** - * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. - * Then initiates the calls to MediaWiki API through an instance of MwVolleyApi. - */ - public void useImageCoords() { - if (decimalCoords != null) { - Timber.d("Decimal coords of image: %s", decimalCoords); - Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image:"+(imageObj==tempImageObj)); - - // Only set cache for this point if image has coords - if (imageObj.imageCoordsExists) { - double decLongitude = imageObj.getDecLongitude(); - double decLatitude = imageObj.getDecLatitude(); - cacheController.setQtPoint(decLongitude, decLatitude); - } - - MwVolleyApi apiCall = new MwVolleyApi(this); - - List displayCatList = cacheController.findCategory(); - boolean catListEmpty = displayCatList.isEmpty(); - - // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories - if (catListEmpty) { - cacheFound = false; - apiCall.request(decimalCoords); - Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList); - } else { - cacheFound = true; - Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); - MwVolleyApi.setGpsCat(displayCatList); - } - }else{ - Timber.d("EXIF: no coords"); - } - - } - @Override public void onPause() { super.onPause(); @@ -698,44 +496,7 @@ public class ShareActivity } //TODO: Move this to a new class. - /* - * Get SHA1 of file from input stream - */ - private String getSHA1(InputStream is) { - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA1"); - } catch (NoSuchAlgorithmException e) { - Timber.e(e, "Exception while getting Digest"); - return ""; - } - - byte[] buffer = new byte[8192]; - int read; - try { - while ((read = is.read(buffer)) > 0) { - digest.update(buffer, 0, read); - } - byte[] md5sum = digest.digest(); - BigInteger bigInt = new BigInteger(1, md5sum); - String output = bigInt.toString(16); - // Fill to 40 chars - output = String.format("%40s", output).replace(' ', '0'); - Timber.i("File SHA1: %s", output); - - return output; - } catch (IOException e) { - Timber.e(e, "IO Exception"); - return ""; - } finally { - try { - is.close(); - } catch (IOException e) { - Timber.e(e, "Exception on closing MD5 input stream"); - } - } - } //TODO: Move this to a new class. Save references to the findViewByIds and pass them to the new method /* From 953d00d2f6f44e26b6ca4399c4c051f232646cbc Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 23:05:29 +1000 Subject: [PATCH 51/94] Fix whitespace --- .../nrw/commons/upload/ShareActivity.java | 38 +++++-------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index bbc278603..f48b5526d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -433,7 +433,6 @@ public class ShareActivity } } - /** * Displays Snackbar to ask for location permissions */ @@ -447,7 +446,6 @@ public class ShareActivity return snackbar; } - //I might not be supposed to change it, but still, I saw it @Override public void onPositiveResponse() { @@ -495,9 +493,6 @@ public class ShareActivity return super.onOptionsItemSelected(item); } - //TODO: Move this to a new class. - - //TODO: Move this to a new class. Save references to the findViewByIds and pass them to the new method /* * function to provide pinch zoom @@ -597,15 +592,10 @@ public class ShareActivity // Construct and run the parallel animation of the four translation and // scale properties (X, Y, SCALE_X, and SCALE_Y). AnimatorSet set = new AnimatorSet(); - set - .play(ObjectAnimator.ofFloat(expandedImageView, View.X, - startBounds.left, finalBounds.left)) - .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, - startBounds.top, finalBounds.top)) - .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, - startScale, 1f)) - .with(ObjectAnimator.ofFloat(expandedImageView, - View.SCALE_Y, startScale, 1f)); + set.play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left, finalBounds.left)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top, finalBounds.top)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f)); set.setDuration(ShortAnimationDuration); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListenerAdapter() { @@ -626,7 +616,6 @@ public class ShareActivity // to the original bounds and show the thumbnail instead of // the expanded image. startScaleFinal = startScale; - } /* @@ -662,17 +651,10 @@ public class ShareActivity // Animate the four positioning/sizing properties in parallel, // back to their original values. AnimatorSet set = new AnimatorSet(); - set.play(ObjectAnimator - .ofFloat(expandedImageView, View.X, startBounds.left)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.Y, startBounds.top)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.SCALE_X, startScaleFinal)) - .with(ObjectAnimator - .ofFloat(expandedImageView, - View.SCALE_Y, startScaleFinal)); + set.play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScaleFinal)) + .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScaleFinal)); set.setDuration(ShortAnimationDuration); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListenerAdapter() { @@ -699,9 +681,7 @@ public class ShareActivity @OnClick(R.id.media_map) public void onFabShowMapsClicked() { if (imageObj != null && imageObj.imageCoordsExists == true) { - Uri gmmIntentUri = Uri - .parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj - .getDecLongitude()); + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); startActivity(mapIntent); From af51ffad8668affd5243ff1933fdc7225cc53c49 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 23:16:57 +1000 Subject: [PATCH 52/94] Incomplete creation of FileProcessor object --- .../nrw/commons/upload/FileProcessor.java | 42 +++--------------- .../free/nrw/commons/upload/GPSExtractor.java | 2 +- .../nrw/commons/upload/ShareActivity.java | 44 +++++++++++++++++-- 3 files changed, 48 insertions(+), 40 deletions(-) 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 b349eab39..6e0d134a0 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 @@ -19,48 +19,20 @@ import java.util.List; import timber.log.Timber; +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; +import static fr.free.nrw.commons.upload.FileUtils.getSHA1; public class FileProcessor { + private Uri mediaUri; - /** - * Check if file user wants to upload already exists on Commons - */ - private void checkIfFileExists() { - if (!useNewPermissions || storagePermitted) { - if (!duplicateCheckPassed) { - //Test SHA1 of image to see if it matches SHA1 of a file on Commons - try { - InputStream inputStream = getContentResolver().openInputStream(mediaUri); - Timber.d("Input stream created from %s", mediaUri.toString()); - String fileSHA1 = getSHA1(inputStream); - Timber.d("File SHA1 is: %s", fileSHA1); + public FileProcessor(Uri mediaUri) { + this.mediaUri = mediaUri; - ExistingFileAsync fileAsyncTask = - new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { - Timber.d("%s duplicate check: %s", mediaUri.toString(), result); - duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); - - //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means - //we are processing images that are already on server???... - if (duplicateCheckPassed) { - //image can be uploaded, so now check if its a useless picture or not - detectUnwantedPictures(); - } - },mwApi); - fileAsyncTask.execute(); - } catch (IOException e) { - Timber.e(e, "IO Exception: "); - } - } - } else { - Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", - useNewPermissions, storagePermitted, locationPermitted); - } } - + /** * Calls the async task that detects if image is fuzzy, too dark, etc */ @@ -108,7 +80,7 @@ public class FileProcessor { * Gets coordinates for category suggestions, either from EXIF data or user location * @param gpsEnabled if true use GPS */ - private void getFileCoordinates(boolean gpsEnabled) { + void getFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { if (imageObj == null) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java index b9750e350..c6863acf6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java @@ -22,7 +22,7 @@ import timber.log.Timber; * is uploaded, extract latitude and longitude from EXIF data of image. If a picture without * geolocation is uploaded, retrieve user's location (if enabled in Settings). */ -public class GPSExtractor { +public class GPSExtractor extends FileProcessor { private final Context context; private SharedPreferences prefs; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index f48b5526d..0effb9350 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -79,6 +79,7 @@ import timber.log.Timber; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; +import static fr.free.nrw.commons.upload.FileUtils.getSHA1; /** * Activity for the title/desc screen after image is selected. Also starts processing image @@ -307,8 +308,9 @@ public class ShareActivity new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERM_ON_CREATE_LOCATION); } + FileProcessor fileObj = new FileProcessor(mediaUri); checkIfFileExists(); - getFileCoordinates(locationPermitted); + fileObj.getFileCoordinates(locationPermitted); SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); @@ -436,9 +438,7 @@ public class ShareActivity /** * Displays Snackbar to ask for location permissions */ - private Snackbar requestPermissionUsingSnackBar(String rationale, - final String[] perms, - final int code) { + private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), rationale, Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok, view -> ActivityCompat.requestPermissions(ShareActivity.this, perms, code)); @@ -446,6 +446,42 @@ public class ShareActivity return snackbar; } + /** + * Check if file user wants to upload already exists on Commons + */ + private void checkIfFileExists() { + if (!useNewPermissions || storagePermitted) { + if (!duplicateCheckPassed) { + //Test SHA1 of image to see if it matches SHA1 of a file on Commons + try { + InputStream inputStream = getContentResolver().openInputStream(mediaUri); + Timber.d("Input stream created from %s", mediaUri.toString()); + String fileSHA1 = getSHA1(inputStream); + Timber.d("File SHA1 is: %s", fileSHA1); + + ExistingFileAsync fileAsyncTask = + new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { + Timber.d("%s duplicate check: %s", mediaUri.toString(), result); + duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); + + //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means + //we are processing images that are already on server???... + if (duplicateCheckPassed) { + //image can be uploaded, so now check if its a useless picture or not + detectUnwantedPictures(); + } + },mwApi); + fileAsyncTask.execute(); + } catch (IOException e) { + Timber.e(e, "IO Exception: "); + } + } + } else { + Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s", + useNewPermissions, storagePermitted, locationPermitted); + } + } + //I might not be supposed to change it, but still, I saw it @Override public void onPositiveResponse() { From 89db8847bf315b5985bb7cae62d42c9009c3e388 Mon Sep 17 00:00:00 2001 From: misaochan Date: Fri, 25 May 2018 23:50:20 +1000 Subject: [PATCH 53/94] Get FileProcessor working by passing context, Uri, etc --- .../nrw/commons/upload/FileProcessor.java | 62 ++++++++++--------- .../nrw/commons/upload/ShareActivity.java | 28 +++++++-- 2 files changed, 55 insertions(+), 35 deletions(-) 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 6e0d134a0..ebd27c09c 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 @@ -1,12 +1,16 @@ package fr.free.nrw.commons.upload; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import java.io.File; @@ -27,22 +31,22 @@ import static fr.free.nrw.commons.upload.FileUtils.getSHA1; public class FileProcessor { private Uri mediaUri; + private ContentResolver contentResolver; + private GPSExtractor imageObj; + private SharedPreferences prefs; + private Context context; + private String decimalCoords; + private boolean haveCheckedForOtherImages = false; + private String filePath; + private boolean useExtStorage; - public FileProcessor(Uri mediaUri) { + FileProcessor(Uri mediaUri, ContentResolver contentResolver, SharedPreferences prefs, Context context) { this.mediaUri = mediaUri; - + this.contentResolver = contentResolver; + this.prefs = prefs; + this.context = context; + useExtStorage = prefs.getBoolean("useExternalStorage", true); } - - /** - * Calls the async task that detects if image is fuzzy, too dark, etc - */ - private void detectUnwantedPictures() { - String imageMediaFilePath = FileUtils.getPath(this,mediaUri); - DetectUnwantedPicturesAsync detectUnwantedPicturesAsync - = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); - detectUnwantedPicturesAsync.execute(); - } - /** * Gets file path from media URI. @@ -50,15 +54,14 @@ public class FileProcessor { * @return file path of media */ @Nullable - private String getPathOfMediaOrCopy() { - String filePath = FileUtils.getPath(getApplicationContext(), mediaUri); + String getPathOfMediaOrCopy() { + filePath = FileUtils.getPath(getApplicationContext(), mediaUri); Timber.d("Filepath: " + filePath); if (filePath == null) { String copyPath = null; try { - ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); + ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); if (descriptor != null) { - boolean useExtStorage = prefs.getBoolean("useExternalStorage", true); if (useExtStorage) { copyPath = FileUtils.createCopyPath(descriptor); return copyPath; @@ -83,21 +86,19 @@ public class FileProcessor { void getFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { - if (imageObj == null) { - ParcelFileDescriptor descriptor = getContentResolver().openFileDescriptor(mediaUri, "r"); + + ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (descriptor != null) { - imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs); + imageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs); } } else { String filePath = getPathOfMediaOrCopy(); if (filePath != null) { - imageObj = new GPSExtractor(filePath, this, prefs); + imageObj = new GPSExtractor(filePath, context, prefs); } - } } - if (imageObj != null) { decimalCoords = imageObj.getCoords(gpsEnabled); if (decimalCoords == null || !imageObj.imageCoordsExists){ //Find other photos taken around the same time which has gps coordinates @@ -107,19 +108,20 @@ public class FileProcessor { else { useImageCoords(); } - } + } catch (FileNotFoundException e) { Timber.w("File not found: " + mediaUri, e); } } - private void findOtherImages(boolean gpsEnabled) { + void findOtherImages(boolean gpsEnabled) { Timber.d("filePath"+getPathOfMediaOrCopy()); - String filePath = getPathOfMediaOrCopy(); + long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created File folder = new File(filePath.substring(0,filePath.lastIndexOf('/'))); File[] files = folder.listFiles(); Timber.d("folderTime Number:"+files.length); + GPSExtractor tempImageObj; for(File file : files){ if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){ @@ -129,17 +131,17 @@ public class FileProcessor { ParcelFileDescriptor descriptor = null; try { - descriptor = getContentResolver().openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r"); + descriptor = contentResolver.openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r"); } catch (FileNotFoundException e) { e.printStackTrace(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (descriptor != null) { - tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(),this, prefs); + tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs); } } else { if (filePath != null) { - tempImageObj = new GPSExtractor(file.getAbsolutePath(), this, prefs); + tempImageObj = new GPSExtractor(file.getAbsolutePath(), context, prefs); } } @@ -149,7 +151,7 @@ public class FileProcessor { // Current image has gps coordinates and it's not current gps locaiton Timber.d("This fild has image coords:"+ file.getAbsolutePath()); // Create a dialog fragment for the suggestion - FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentManager fragmentManager = (Fragment) context.getSupportFragmentManager(); SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); Bundle args = new Bundle(); args.putString("originalImagePath",filePath); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 0effb9350..31a942ad6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -135,6 +135,7 @@ public class ShareActivity private GPSExtractor imageObj; private GPSExtractor tempImageObj; private String decimalCoords; + private FileProcessor fileObj; private boolean useNewPermissions = false; private boolean storagePermitted = false; @@ -145,7 +146,7 @@ public class ShareActivity private Snackbar snackbar; private boolean duplicateCheckPassed = false; - private boolean haveCheckedForOtherImages = false; + private boolean isNearbyUpload = false; private Animator CurrentAnimator; @@ -197,7 +198,7 @@ public class ShareActivity * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController */ private void uploadBegins() { - getFileCoordinates(locationPermitted); + fileObj.getFileCoordinates(locationPermitted); Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG); startingToast.show(); @@ -308,7 +309,13 @@ public class ShareActivity new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERM_ON_CREATE_LOCATION); } - FileProcessor fileObj = new FileProcessor(mediaUri); + + ContentResolver contentResolver = this.getContentResolver(); + + fileObj = new FileProcessor(mediaUri, contentResolver, prefs, this); + String filePath = fileObj.getPathOfMediaOrCopy(); + + checkIfFileExists(); fileObj.getFileCoordinates(locationPermitted); @@ -488,16 +495,27 @@ public class ShareActivity imageObj = tempImageObj; decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data Timber.d("EXIF from tempImageObj"); - useImageCoords(); + fileObj.useImageCoords(); } @Override public void onNegativeResponse() { Timber.d("EXIF from imageObj"); - useImageCoords(); + fileObj.useImageCoords(); } + /** + * Calls the async task that detects if image is fuzzy, too dark, etc + */ + private void detectUnwantedPictures() { + String imageMediaFilePath = FileUtils.getPath(this, mediaUri); + DetectUnwantedPicturesAsync detectUnwantedPicturesAsync + = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); + detectUnwantedPicturesAsync.execute(); + } + + @Override public void onPause() { super.onPause(); From bf61d2d8b24f3d3c7b207af6a03257a2ae8e2958 Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:01:39 +1000 Subject: [PATCH 54/94] Remove all compile errors --- .../nrw/commons/upload/FileProcessor.java | 25 +++++++++++++------ .../free/nrw/commons/upload/GPSExtractor.java | 2 +- .../nrw/commons/upload/ShareActivity.java | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) 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 ebd27c09c..2baa8392e 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 @@ -12,6 +12,7 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; +import android.support.v7.app.AppCompatActivity; import java.io.File; import java.io.FileNotFoundException; @@ -21,6 +22,9 @@ import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; +import javax.inject.Inject; + +import fr.free.nrw.commons.caching.CacheController; import timber.log.Timber; import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; @@ -39,6 +43,10 @@ public class FileProcessor { private boolean haveCheckedForOtherImages = false; private String filePath; private boolean useExtStorage; + private boolean cacheFound; + + @Inject + CacheController cacheController; FileProcessor(Uri mediaUri, ContentResolver contentResolver, SharedPreferences prefs, Context context) { this.mediaUri = mediaUri; @@ -86,7 +94,6 @@ public class FileProcessor { void getFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { - ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (descriptor != null) { @@ -128,8 +135,7 @@ public class FileProcessor { //Make sure the photos were taken within 20seconds Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation); tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos - ParcelFileDescriptor descriptor - = null; + ParcelFileDescriptor descriptor = null; try { descriptor = contentResolver.openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r"); } catch (FileNotFoundException e) { @@ -149,15 +155,14 @@ public class FileProcessor { Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled)); if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){ // Current image has gps coordinates and it's not current gps locaiton - Timber.d("This fild has image coords:"+ file.getAbsolutePath()); + Timber.d("This file has image coords:"+ file.getAbsolutePath()); // Create a dialog fragment for the suggestion - FragmentManager fragmentManager = (Fragment) context.getSupportFragmentManager(); SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); Bundle args = new Bundle(); args.putString("originalImagePath",filePath); args.putString("possibleImagePath",file.getAbsolutePath()); newFragment.setArguments(args); - newFragment.show(fragmentManager, "dialog"); + newFragment.show(((AppCompatActivity)context).getSupportFragmentManager(), "dialog"); break; } } @@ -175,7 +180,7 @@ public class FileProcessor { public void useImageCoords() { if (decimalCoords != null) { Timber.d("Decimal coords of image: %s", decimalCoords); - Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image:"+(imageObj==tempImageObj)); + Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image"); // Only set cache for this point if image has coords if (imageObj.imageCoordsExists) { @@ -184,11 +189,12 @@ public class FileProcessor { cacheController.setQtPoint(decLongitude, decLatitude); } - MwVolleyApi apiCall = new MwVolleyApi(this); + MwVolleyApi apiCall = new MwVolleyApi(context); List displayCatList = cacheController.findCategory(); boolean catListEmpty = displayCatList.isEmpty(); + // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories if (catListEmpty) { cacheFound = false; @@ -202,7 +208,10 @@ public class FileProcessor { }else{ Timber.d("EXIF: no coords"); } + } + boolean isCacheFound() { + return cacheFound; } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java index c6863acf6..b9750e350 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/GPSExtractor.java @@ -22,7 +22,7 @@ import timber.log.Timber; * is uploaded, extract latitude and longitude from EXIF data of image. If a picture without * geolocation is uploaded, retrieve user's location (if enabled in Settings). */ -public class GPSExtractor extends FileProcessor { +public class GPSExtractor { private final Context context; private SharedPreferences prefs; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 31a942ad6..b32c8b9d2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -203,7 +203,7 @@ public class ShareActivity Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG); startingToast.show(); - if (!cacheFound) { + if (!fileObj.isCacheFound()) { //Has to be called after apiCall.request() cacheController.cacheCategory(); Timber.d("Cache the categories found"); From bc1adca999e4a1c6c6f59a5038043eda6733fe90 Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:03:48 +1000 Subject: [PATCH 55/94] Add Dagger usage to FileProcessor --- .../fr/free/nrw/commons/di/CommonsApplicationComponent.java | 3 +++ .../main/java/fr/free/nrw/commons/upload/FileProcessor.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java index 91f6d4ccb..d41058199 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java @@ -16,6 +16,7 @@ import fr.free.nrw.commons.delete.DeleteTask; import fr.free.nrw.commons.modifications.ModificationsSyncAdapter; import fr.free.nrw.commons.settings.SettingsFragment; import fr.free.nrw.commons.nearby.PlaceRenderer; +import fr.free.nrw.commons.upload.FileProcessor; @Singleton @Component(modules = { @@ -47,6 +48,8 @@ public interface CommonsApplicationComponent extends AndroidInjector Date: Sat, 26 May 2018 00:17:16 +1000 Subject: [PATCH 56/94] Fixed NPE when uploading --- .../fr/free/nrw/commons/upload/FileProcessor.java | 2 +- .../fr/free/nrw/commons/upload/ShareActivity.java | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) 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 c78312246..8b28aef49 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 @@ -65,7 +65,7 @@ public class FileProcessor { */ @Nullable String getPathOfMediaOrCopy() { - filePath = FileUtils.getPath(getApplicationContext(), mediaUri); + filePath = FileUtils.getPath(context, mediaUri); Timber.d("Filepath: " + filePath); if (filePath == null) { String copyPath = null; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index b32c8b9d2..2822822f9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -268,6 +268,7 @@ public class ShareActivity finish(); } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -312,13 +313,6 @@ public class ShareActivity ContentResolver contentResolver = this.getContentResolver(); - fileObj = new FileProcessor(mediaUri, contentResolver, prefs, this); - String filePath = fileObj.getPathOfMediaOrCopy(); - - - checkIfFileExists(); - fileObj.getFileCoordinates(locationPermitted); - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); if (shareView == null && categorizationFragment == null) { @@ -329,6 +323,10 @@ public class ShareActivity .commitAllowingStateLoss(); } uploadController.prepareService(); + + fileObj = new FileProcessor(mediaUri, contentResolver, prefs, this); + checkIfFileExists(); + fileObj.getFileCoordinates(locationPermitted); } /** From cc678baa5d6bc997eb7d789bf0b12f2fca6da18a Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:25:02 +1000 Subject: [PATCH 57/94] Optimize imports --- .../free/nrw/commons/upload/FileProcessor.java | 17 +++-------------- .../free/nrw/commons/upload/ShareActivity.java | 16 +++------------- 2 files changed, 6 insertions(+), 27 deletions(-) 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 8b28aef49..a66495eed 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 @@ -1,6 +1,5 @@ package fr.free.nrw.commons.upload; -import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; @@ -9,16 +8,11 @@ import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; -import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; @@ -29,9 +23,6 @@ import fr.free.nrw.commons.di.ApplicationlessInjection; import timber.log.Timber; import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; -import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED; -import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE; -import static fr.free.nrw.commons.upload.FileUtils.getSHA1; public class FileProcessor { @@ -64,7 +55,7 @@ public class FileProcessor { * @return file path of media */ @Nullable - String getPathOfMediaOrCopy() { + private String getPathOfMediaOrCopy() { filePath = FileUtils.getPath(context, mediaUri); Timber.d("Filepath: " + filePath); if (filePath == null) { @@ -123,7 +114,7 @@ public class FileProcessor { } } - void findOtherImages(boolean gpsEnabled) { + private void findOtherImages(boolean gpsEnabled) { Timber.d("filePath"+getPathOfMediaOrCopy()); long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created @@ -156,9 +147,8 @@ public class FileProcessor { if(tempImageObj!=null){ Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled)); if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){ -// Current image has gps coordinates and it's not current gps locaiton + // Current image has gps coordinates and it's not current gps locaiton Timber.d("This file has image coords:"+ file.getAbsolutePath()); -// Create a dialog fragment for the suggestion SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); Bundle args = new Bundle(); args.putString("originalImagePath",filePath); @@ -171,7 +161,6 @@ public class FileProcessor { } } haveCheckedForOtherImages = true; //Finished checking for other images - return; } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 2822822f9..29dd64369 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -1,11 +1,11 @@ package fr.free.nrw.commons.upload; import android.Manifest; -import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -19,16 +19,13 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.app.ActivityCompat; -import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.BitmapCompat; import android.util.Log; @@ -38,28 +35,22 @@ import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.Toast; -import butterknife.BindView; -import butterknife.OnClick; import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; import com.facebook.drawee.view.SimpleDraweeView; import com.github.chrisbanes.photoview.PhotoView; - -import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; -import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Date; import java.util.List; import javax.inject.Inject; import javax.inject.Named; +import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.auth.SessionManager; @@ -72,7 +63,6 @@ import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; - import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; From 11c35b55fcd7805c4b6a0ec11953329bf7923879 Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:30:57 +1000 Subject: [PATCH 58/94] Tidy up code --- .../nrw/commons/upload/ShareActivity.java | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 29dd64369..826c809e5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -80,6 +80,14 @@ public class ShareActivity implements SingleUploadFragment.OnUploadActionInitiated, OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse { + private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; + private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; + private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; + private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; + //Had to make them class variables, to extract out the click listeners, also I see no harm in this + final Rect startBounds = new Rect(); + final Rect finalBounds = new Rect(); + final Point globalOffset = new Point(); @Inject MediaWikiApi mwApi; @Inject @@ -93,7 +101,6 @@ public class ShareActivity @Inject @Named("default_preferences") SharedPreferences prefs; - @BindView(R.id.container) FrameLayout flContainer; @BindView(R.id.backgroundImage) @@ -108,45 +115,27 @@ public class ShareActivity FloatingActionButton mainFab; @BindView(R.id.expanded_image) PhotoView expandedImageView; - - private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; - private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; - private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; - private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; - private String source; private String mimeType; - private CategorizationFragment categorizationFragment; private Uri mediaUri; private Contribution contribution; private boolean cacheFound; - private GPSExtractor imageObj; private GPSExtractor tempImageObj; private String decimalCoords; private FileProcessor fileObj; - private boolean useNewPermissions = false; private boolean storagePermitted = false; private boolean locationPermitted = false; - private String title; private String description; private Snackbar snackbar; private boolean duplicateCheckPassed = false; - - private boolean isNearbyUpload = false; - private Animator CurrentAnimator; private long ShortAnimationDuration; private boolean isFABOpen = false; - - //Had to make them class variables, to extract out the click listeners, also I see no harm in this - final Rect startBounds = new Rect(); - final Rect finalBounds = new Rect(); - final Point globalOffset = new Point(); private float startScaleFinal; /** @@ -274,14 +263,13 @@ public class ShareActivity R.drawable.ic_error_outline_black_24dp, getTheme())) .build()); - receiveIntent(); + receiveImageIntent(); if (savedInstanceState != null) { contribution = savedInstanceState.getParcelable("contribution"); } requestAuthToken(); - Timber.d("Uri: %s", mediaUri.toString()); Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory()); @@ -301,8 +289,6 @@ public class ShareActivity REQUEST_PERM_ON_CREATE_LOCATION); } - ContentResolver contentResolver = this.getContentResolver(); - SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView"); categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization"); if (shareView == null && categorizationFragment == null) { @@ -314,6 +300,7 @@ public class ShareActivity } uploadController.prepareService(); + ContentResolver contentResolver = this.getContentResolver(); fileObj = new FileProcessor(mediaUri, contentResolver, prefs, this); checkIfFileExists(); fileObj.getFileCoordinates(locationPermitted); @@ -322,7 +309,7 @@ public class ShareActivity /** * Receive intent from ContributionController.java when user selects picture to upload */ - private void receiveIntent() { + private void receiveImageIntent() { Intent intent = getIntent(); if (Intent.ACTION_SEND.equals(intent.getAction())) { @@ -450,19 +437,16 @@ public class ShareActivity //Test SHA1 of image to see if it matches SHA1 of a file on Commons try { InputStream inputStream = getContentResolver().openInputStream(mediaUri); - Timber.d("Input stream created from %s", mediaUri.toString()); String fileSHA1 = getSHA1(inputStream); + Timber.d("Input stream created from %s", mediaUri.toString()); Timber.d("File SHA1 is: %s", fileSHA1); ExistingFileAsync fileAsyncTask = new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> { Timber.d("%s duplicate check: %s", mediaUri.toString(), result); duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); - - //TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means - //we are processing images that are already on server???... if (duplicateCheckPassed) { - //image can be uploaded, so now check if its a useless picture or not + //image is not a duplicate, so now check if its a unwanted picture or not detectUnwantedPictures(); } },mwApi); @@ -477,6 +461,7 @@ public class ShareActivity } } + //I might not be supposed to change it, but still, I saw it @Override public void onPositiveResponse() { @@ -503,7 +488,6 @@ public class ShareActivity detectUnwantedPicturesAsync.execute(); } - @Override public void onPause() { super.onPause(); From db6ade0d1e6f3f965482f0c88e31503a641b9363 Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:32:33 +1000 Subject: [PATCH 59/94] Move detectUnwantedPictures() to FileProcessor --- .../fr/free/nrw/commons/upload/FileProcessor.java | 14 +++++++++++++- .../fr/free/nrw/commons/upload/ShareActivity.java | 13 +------------ 2 files changed, 14 insertions(+), 13 deletions(-) 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 a66495eed..5007fe8c4 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 @@ -1,5 +1,6 @@ package fr.free.nrw.commons.upload; +import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.SharedPreferences; @@ -13,6 +14,7 @@ import android.support.v7.app.AppCompatActivity; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.Date; import java.util.List; @@ -196,7 +198,7 @@ public class FileProcessor { Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList); MwVolleyApi.setGpsCat(displayCatList); } - }else{ + } else { Timber.d("EXIF: no coords"); } } @@ -205,4 +207,14 @@ public class FileProcessor { return cacheFound; } + /** + * Calls the async task that detects if image is fuzzy, too dark, etc + */ + void detectUnwantedPictures() { + String imageMediaFilePath = FileUtils.getPath(context, mediaUri); + DetectUnwantedPicturesAsync detectUnwantedPicturesAsync + = new DetectUnwantedPicturesAsync(new WeakReference((Activity) context), imageMediaFilePath); + detectUnwantedPicturesAsync.execute(); + } + } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 826c809e5..b741783e0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -447,7 +447,7 @@ public class ShareActivity duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE); if (duplicateCheckPassed) { //image is not a duplicate, so now check if its a unwanted picture or not - detectUnwantedPictures(); + fileObj.detectUnwantedPictures(); } },mwApi); fileAsyncTask.execute(); @@ -475,17 +475,6 @@ public class ShareActivity public void onNegativeResponse() { Timber.d("EXIF from imageObj"); fileObj.useImageCoords(); - - } - - /** - * Calls the async task that detects if image is fuzzy, too dark, etc - */ - private void detectUnwantedPictures() { - String imageMediaFilePath = FileUtils.getPath(this, mediaUri); - DetectUnwantedPicturesAsync detectUnwantedPicturesAsync - = new DetectUnwantedPicturesAsync(new WeakReference(this), imageMediaFilePath); - detectUnwantedPicturesAsync.execute(); } @Override From 47f7ee6849d5b6bc8711956ef6cb09af8d1ab761 Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:34:43 +1000 Subject: [PATCH 60/94] Copy onPositiveResponse() etc to FileProcessor --- .../free/nrw/commons/upload/FileProcessor.java | 18 ++++++++++++++++-- .../free/nrw/commons/upload/ShareActivity.java | 16 +--------------- 2 files changed, 17 insertions(+), 17 deletions(-) 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 5007fe8c4..859cd2c9e 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 @@ -26,7 +26,7 @@ import timber.log.Timber; import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; -public class FileProcessor { +public class FileProcessor implements SimilarImageDialogFragment.onResponse{ private Uri mediaUri; private ContentResolver contentResolver; @@ -38,6 +38,7 @@ public class FileProcessor { private String filePath; private boolean useExtStorage; private boolean cacheFound; + private GPSExtractor tempImageObj; @Inject CacheController cacheController; @@ -123,7 +124,7 @@ public class FileProcessor { File folder = new File(filePath.substring(0,filePath.lastIndexOf('/'))); File[] files = folder.listFiles(); Timber.d("folderTime Number:"+files.length); - GPSExtractor tempImageObj; + for(File file : files){ if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){ @@ -217,4 +218,17 @@ public class FileProcessor { detectUnwantedPicturesAsync.execute(); } + @Override + public void onPositiveResponse() { + imageObj = tempImageObj; + decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data + Timber.d("EXIF from tempImageObj"); + useImageCoords(); + } + + @Override + public void onNegativeResponse() { + Timber.d("EXIF from imageObj"); + useImageCoords(); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index b741783e0..c3ba939e9 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -78,7 +78,7 @@ import static fr.free.nrw.commons.upload.FileUtils.getSHA1; public class ShareActivity extends AuthenticatedActivity implements SingleUploadFragment.OnUploadActionInitiated, - OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse { + OnCategoriesSaveHandler { private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; @@ -462,20 +462,6 @@ public class ShareActivity } - //I might not be supposed to change it, but still, I saw it - @Override - public void onPositiveResponse() { - imageObj = tempImageObj; - decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data - Timber.d("EXIF from tempImageObj"); - fileObj.useImageCoords(); - } - - @Override - public void onNegativeResponse() { - Timber.d("EXIF from imageObj"); - fileObj.useImageCoords(); - } @Override public void onPause() { From b1055f2fbab20ad6db3d9f72569b283419ace48e Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:42:10 +1000 Subject: [PATCH 61/94] Started moving to ZoomUtils.java --- .../fr/free/nrw/commons/upload/ZoomUtils.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java new file mode 100644 index 000000000..74605e063 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java @@ -0,0 +1,96 @@ +package fr.free.nrw.commons.upload; + +import android.graphics.Bitmap; +import android.graphics.BitmapRegionDecoder; +import android.graphics.Rect; +import android.provider.MediaStore; +import android.support.v4.graphics.BitmapCompat; +import android.view.View; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +public class ZoomUtils { + + static void zoomImageUtil(View thumbView, Rect startBounds, InputStream input) { + + Bitmap scaled = null; + BitmapRegionDecoder decoder = null; + try { + decoder = BitmapRegionDecoder.newInstance(input, false); + } catch (IOException e) { + e.printStackTrace(); + } + Bitmap bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null); + try { + //Compress the Image + System.gc(); + Runtime rt = Runtime.getRuntime(); + long maxMemory = rt.freeMemory(); + bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageuri); + int bitmapByteCount= BitmapCompat.getAllocationByteCount(bitmap); + long height = bitmap.getHeight(); + long width = bitmap.getWidth(); + long calHeight = (long) ((height * maxMemory)/(bitmapByteCount * 1.1)); + long calWidth = (long) ((width * maxMemory)/(bitmapByteCount * 1.1)); + scaled = Bitmap.createScaledBitmap(bitmap,(int) Math.min(width,calWidth), (int) Math.min(height,calHeight), true); + } catch (IOException e) { + } catch (NullPointerException e){ + scaled = bitmap; + } + // Load the high-resolution "zoomed-in" image. + expandedImageView.setImageBitmap(scaled); + + + + // Calculate the starting and ending bounds for the zoomed-in image. + // This step involves lots of math. Yay, math. + // The start bounds are the global visible rectangle of the thumbnail, + // and the final bounds are the global visible rectangle of the container + // view. Also set the container view's offset as the origin for the + // bounds, since that's the origin for the positioning animation + // properties (X, Y). + thumbView.getGlobalVisibleRect(startBounds); + flContainer.getGlobalVisibleRect(finalBounds, globalOffset); + startBounds.offset(-globalOffset.x, -globalOffset.y); + finalBounds.offset(-globalOffset.x, -globalOffset.y); + + // Adjust the start bounds to be the same aspect ratio as the final + // bounds using the "center crop" technique. This prevents undesirable + // stretching during the animation. Also calculate the start scaling + // factor (the end scaling factor is always 1.0). + float startScale; + if ((float) finalBounds.width() / finalBounds.height() + > (float) startBounds.width() / startBounds.height()) { + // Extend start bounds horizontally + startScale = (float) startBounds.height() / finalBounds.height(); + float startWidth = startScale * finalBounds.width(); + float deltaWidth = (startWidth - startBounds.width()) / 2; + startBounds.left -= deltaWidth; + startBounds.right += deltaWidth; + } else { + // Extend start bounds vertically + startScale = (float) startBounds.width() / finalBounds.width(); + float startHeight = startScale * finalBounds.height(); + float deltaHeight = (startHeight - startBounds.height()) / 2; + startBounds.top -= deltaHeight; + startBounds.bottom += deltaHeight; + } + + // Hide the thumbnail and show the zoomed-in view. When the animation + // begins, it will position the zoomed-in view in the place of the + // thumbnail. + thumbView.setAlpha(0f); + expandedImageView.setVisibility(View.VISIBLE); + zoomOutButton.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.GONE); + + // Set the pivot point for SCALE_X and SCALE_Y transformations + // to the top-left corner of the zoomed-in view (the default + // is the center of the view). + expandedImageView.setPivotX(0f); + expandedImageView.setPivotY(0f); + + } +} From 3816d81daf76356de58ded2f7bc860a81c7960ec Mon Sep 17 00:00:00 2001 From: misaochan Date: Sat, 26 May 2018 00:42:27 +1000 Subject: [PATCH 62/94] Remove Zoom code from ShareActivity --- .../nrw/commons/upload/ShareActivity.java | 81 +------------------ 1 file changed, 4 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index c3ba939e9..3d7006d58 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -499,96 +499,23 @@ public class ShareActivity * function to provide pinch zoom */ private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { - // If there's an animation in progress, cancel it - // immediately and proceed with this one. + // If there's an animation in progress, cancel it immediately and proceed with this one. if (CurrentAnimator != null) { CurrentAnimator.cancel(); } ViewUtil.hideKeyboard(ShareActivity.this.findViewById(R.id.titleEdit | R.id.descEdit)); closeFABMenu(); mainFab.setVisibility(View.GONE); + InputStream input = null; - Bitmap scaled = null; + try { input = this.getContentResolver().openInputStream(imageuri); } catch (FileNotFoundException e) { e.printStackTrace(); } - BitmapRegionDecoder decoder = null; - try { - decoder = BitmapRegionDecoder.newInstance(input, false); - } catch (IOException e) { - e.printStackTrace(); - } - Bitmap bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null); - try { - //Compress the Image - System.gc(); - Runtime rt = Runtime.getRuntime(); - long maxMemory = rt.freeMemory(); - bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageuri); - int bitmapByteCount= BitmapCompat.getAllocationByteCount(bitmap); - long height = bitmap.getHeight(); - long width = bitmap.getWidth(); - long calHeight = (long) ((height * maxMemory)/(bitmapByteCount * 1.1)); - long calWidth = (long) ((width * maxMemory)/(bitmapByteCount * 1.1)); - scaled = Bitmap.createScaledBitmap(bitmap,(int) Math.min(width,calWidth), (int) Math.min(height,calHeight), true); - } catch (IOException e) { - } catch (NullPointerException e){ - scaled = bitmap; - } - // Load the high-resolution "zoomed-in" image. - expandedImageView.setImageBitmap(scaled); - - - // Calculate the starting and ending bounds for the zoomed-in image. - // This step involves lots of math. Yay, math. - // The start bounds are the global visible rectangle of the thumbnail, - // and the final bounds are the global visible rectangle of the container - // view. Also set the container view's offset as the origin for the - // bounds, since that's the origin for the positioning animation - // properties (X, Y). - thumbView.getGlobalVisibleRect(startBounds); - flContainer.getGlobalVisibleRect(finalBounds, globalOffset); - startBounds.offset(-globalOffset.x, -globalOffset.y); - finalBounds.offset(-globalOffset.x, -globalOffset.y); - - // Adjust the start bounds to be the same aspect ratio as the final - // bounds using the "center crop" technique. This prevents undesirable - // stretching during the animation. Also calculate the start scaling - // factor (the end scaling factor is always 1.0). - float startScale; - if ((float) finalBounds.width() / finalBounds.height() - > (float) startBounds.width() / startBounds.height()) { - // Extend start bounds horizontally - startScale = (float) startBounds.height() / finalBounds.height(); - float startWidth = startScale * finalBounds.width(); - float deltaWidth = (startWidth - startBounds.width()) / 2; - startBounds.left -= deltaWidth; - startBounds.right += deltaWidth; - } else { - // Extend start bounds vertically - startScale = (float) startBounds.width() / finalBounds.width(); - float startHeight = startScale * finalBounds.height(); - float deltaHeight = (startHeight - startBounds.height()) / 2; - startBounds.top -= deltaHeight; - startBounds.bottom += deltaHeight; - } - - // Hide the thumbnail and show the zoomed-in view. When the animation - // begins, it will position the zoomed-in view in the place of the - // thumbnail. - thumbView.setAlpha(0f); - expandedImageView.setVisibility(View.VISIBLE); - zoomOutButton.setVisibility(View.VISIBLE); - zoomInButton.setVisibility(View.GONE); - - // Set the pivot point for SCALE_X and SCALE_Y transformations - // to the top-left corner of the zoomed-in view (the default - // is the center of the view). - expandedImageView.setPivotX(0f); - expandedImageView.setPivotY(0f); + ZoomUtils.zoomImageUtil(thumbView, startBounds, input); // Construct and run the parallel animation of the four translation and // scale properties (X, Y, SCALE_X, and SCALE_Y). From ae1cc86b03041921c2d6fcc5596153c85d1b3dff Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Fri, 25 May 2018 23:10:12 +0530 Subject: [PATCH 63/94] Fixfor#1493 (#1548) Update gradle dependencies --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d2b9463c1..7faf3661d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ dependencies { implementation 'com.jakewharton.timber:timber:4.5.1' implementation 'info.debatty:java-string-similarity:0.24' implementation 'com.borjabravo:readmoretextview:2.1.0' - implementation 'com.android.support.constraint:constraint-layout:1.0.2' + implementation 'com.android.support.constraint:constraint-layout:1.1.0' implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){ transitive=true } @@ -76,7 +76,7 @@ dependencies { androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION" - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2-alpha1' + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY" From 89418403f9edfdf10e151a3e9af3c9e298da27eb Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 14:16:29 +0530 Subject: [PATCH 64/94] fixed featured image back bug --- .../nrw/commons/category/CategoryImagesListFragment.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 3b6734edd..18eb5ec48 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -224,4 +224,10 @@ public class CategoryImagesListFragment extends DaggerFragment { public ListAdapter getAdapter() { return gridView.getAdapter(); } + + @Override + public void onResume() { + gridView.setAdapter(gridAdapter); + super.onResume(); + } } From ac8bb10a1475d6ba6b1480be8f9d68e9c4b78ef1 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 28 May 2018 10:51:54 +0200 Subject: [PATCH 65/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 2 +- app/src/main/res/values-hr/error.xml | 10 + app/src/main/res/values-hr/strings.xml | 275 +++++++++++++++++++++ app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 3 +- app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 11 +- 9 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 app/src/main/res/values-hr/error.xml create mode 100644 app/src/main/res/values-hr/strings.xml diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 14444cc4a..3002482a5 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -57,6 +57,7 @@ Παρακαλώ παρέχετε ένα τίτλο για αυτό το αρχείο Περιγραφή Δεν είναι δυνατή η σύνδεση - αποτυχία του δικτύου + Αποτυχία σύνδεσης - παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό σας Πάρα πολλές ανεπιτυχείς προσπάθειες. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά. Συγνώμη, αυτός ο χρήστης έχει αποκλειστεί από τα Commons Πρέπει να δώσετε τον κωδικό πιστοποίησης με δύο παράγοντες diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 804c8755d..1be0ebd1a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -58,7 +58,7 @@ Proporciona un título para este archivo Descripción No se pudo iniciar sesión: falla de red - Incapaz de ingresar - por favor cheque su nombre de usario y contraseña + No se puede acceder. Revisa el nombre de usuario y la contraseña Demasiados intentos fallidos. Inténtalo de nuevo en unos minutos. Lo sentimos, este usuario ha sido bloqueado en Commons Debes proporcionar tu código de auntenticación de dos factores. diff --git a/app/src/main/res/values-hr/error.xml b/app/src/main/res/values-hr/error.xml new file mode 100644 index 000000000..4bc77982c --- /dev/null +++ b/app/src/main/res/values-hr/error.xml @@ -0,0 +1,10 @@ + + + + Aplikacija Zajednički poslužitelj je prestala s radom + Nešto je krenulo po krivu! + Napišite nam što radite i podijelite s nama putem elektroničke pošte. Pomoći će nam da to popravimo! + Hvala Vam! + diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml new file mode 100644 index 000000000..f675e2f77 --- /dev/null +++ b/app/src/main/res/values-hr/strings.xml @@ -0,0 +1,275 @@ + + + + Izgled + Opće + Povratna informacija + Lokacija + Zajednički poslužitelj + + Postavke + Suradničko ime + Zaporka + Prijavite se na Commons beta račun + Prijavi se + Zaboravljena zaporka? + Otvori račun + Prijava + Molimo pričekajte ... + Prijava uspješna! + Prijava neuspješna! + Datoteka nije pronađena. Molimo probajte drugu. + Autentifikacija neuspješna! + Postavljanje započeto! + %1$s postavljeno! + Dodirnite da biste vidjeli datoteku + Počinje postavljanje %1$s + Postavljanje %1$s + Završeno postavljanje %1$s + Postavljanje %1$s neuspješno + Dodirnite da biste vidjeli + + Postavlja se %1$d datoteka + Postavljaju se %1$d datoteke + + Moja nedavja postavljanja + U redu čekanja + Neuspješno + %1$d%% postavljeno + Postavljanje + Iz galerije + Napravi sliku + U blizini + Moja postavljanja + Podijeli + Pogledaj u pregledniku + Naziv + Molimo imenujte ovu datoteku + Opis + Prijava nije moguća - mrežna pogrješka + Prijava nije moguća - molimo provjerite suradničko ime i zaportku + Previše neuspješnih pokušaja, molimo probajte opet za par minuta. + Ispričavamo se, ovaj je suradnik blokiran na Zajendičkom poslužitelju + Morate upisati autetifikacijski kôd od dva faktora + Prijava neuspješna + Postavljanje + Imenujte ovaj set + Promjene + Postavljanje + Pretraži kategorije + Spremi + Osvježi + Popis + GPS je onemogućen na Vašem uređaju. Želite li ga omogućiti? + Omogući GPS + Nemate još postavljenih datoteka + + \@string/contributions_subtitle_zero + %1$d postavljena datoteka + %1$d postavljene datoteke + + + Započeto %1$d postavljanje + Započeta %1$d postavljanja + + + %1$d postavljanje + %1$d postavljanja + + Nema kategorija koje odgovoraju upitu %1$s + Dodajte slikama kategorije kako bi se lakše pronašle. Da biste ih dodali, započnite s upisivanjem. + Kategorije + Postavke + Otvori račun + Izabrane slike + O + Aplikacija The Wikimedia Commons je aplikacija otvorenog kôda koju razvijaju i održavaju volonteri Wikimedijine zajednice. Zaklada Wikimedija nije uključena u stvaranje, razvoj ili održavanje ove aplikacije. + Da biste prijavili poteškoću ili dali prijedlog, stvorite <a href=\"https://github.com/commons-app/apps-android-commons/issues\">novi zahtjev na GitHubu</a>. + <u>Politika privatnosti</u> + <u>Zasluge</u> + O + Pošaljite povratnu informaciju (putem elektroničke pošte) + Klijent za elektroničku poštu nije instaliran + Nedavno rabljene kategorije + Pričekajte za prvu sinkronizaciju... + Nemate još postavljenih slika. + Pokušaj ponovo + Odustani + Ova će slika biti licencirana pod %1$s + Slanjem ove slike izjavljujem da je ona moje djelo i ne sadrži materijale zaštićene autorskim pravom ili selfije, te da je u skladu sa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">smjernicama Zajedničkog poslužitelja</a>. + Preuzmi + Podrazumijevana licencija + Rabite prethodni naziv/opis + Automatski pribavi trenutačnu lokaciju + Ako slika nema oznaku lokacije, pribavi trenutačnu lokaciju kako bi se mogle ponuditi kategorije + Noćni način + Rabi tamnu temu + Imenovanje-Dijeli pod istim uvjetima 4.0 + Imenovanje 4.0 + Imenovanje-Dijeli pod istim uvjetima 3.0 + Imenovanje 3.0 + CC0 + CC BY-SA 3.0 + CC BY-SA 3.0 (Austrija) + CC BY-SA 3.0 (Njemačka) + CC BY-SA 3.0 (Estonija) + CC BY-SA 3.0 (Španjolska) + CC BY-SA 3.0 (Hrvatska) + CC BY-SA 3.0 (Luksemburg) + CC BY-SA 3.0 (Nizozemska) + CC BY-SA 3.0 (Norveška) + CC BY-SA 3.0 (Poljska) + CC BY-SA 3.0 (Rumunjska) + CC BY 3.0 + CC BY-SA 4.0 + CC BY 4.0 + CC Zero + Na Zajedničkom poslužitelju se nalazi većina slika rabljena na Wikipediji. + Vaše slike pomažu u edukaciji ljudi diljem svijeta! + Molimo postavite slike koje su u cijelosti Vaše djelo: + Objekti iz prirode (cvijeće, životinje, planine)\n• Korisni objekti (bicikla, željezničke postaje)\n• Poznate osobe (Vaš gradonačelnik, olimpijski sportaš kojeg ste sreli) + Objekti iz prirode (cvijeće, životinje, planine) + Korisni objekti (bicikla, željezničke postaje) + Poznate osobe (Vaš gradonačelnik, olimpijski sportaš kojeg ste sreli) + Molimo NE postavljajte: + - selfije ili slike Vaših prijatelja\n- slike koje ste preuzeli s interneta\n- snimke ekrana zaštićenih aplikacija + Selfije ili slike Vaših prijatelja + Slike koje ste preuzeli s interneta + Snimke ekrana zaštićenih aplikacija + Primjer postavljanja: + - Naziv: Sydneyska opera\n- Opis: Sydneyska opera viđena iz zaljeva\n- Kategorije: Sydney Opera House from the west, Sydney Opera House remote views + Naziv: Sydneyska opera + Opis: Sydneyska opera viđena iz zaljeva + Kategorije: Sydney Opera House from the west, Sydney Opera House remote views + Dijelite Vaše slike. Pomozite da članci na Wikipediji zažive! + Slike na wikipediji su sa Zajedničkog poslužitelja. + Vaše slike pomažu u edukaciji ljudi diljem svijeta. + Izbjegavajte materijale s autorskim pravima koje ste pronašli na internetu (slike plakata, naslovnice knjiga, i slično). + Jeste li razumjeli? + Da! + Kategorije + Učitavanje... + Ništa nije odabrano + Nema opisa + Nepoznata licencija + Osvježi + Potrebno dopuštenje čitanja vanjske pohrane. Bez toga aplikacija ne može pristupiti Vašoj galeriji. + Potrebno dopuštenje spremanja na vanjsku pohranu. Bez toga aplikacija ne može pristupiti Vašoj kameri. + Potrebno dopuštenje za određivanje trenutačne lokacije za prijedloge kategorija (nije obvezno) + U redu + Mjesta u blizini + Nisu pronađena mjesta u blizini + Upozorenje + Ova datoteka već postoji na Zajedničkom poslužitelju. Jeste li sigurni da želite nastaviti? + Da + Ne + Naslov + Naslov medija + Opis + Ovdje ide opis datoteke. Mogao bi biti poprilično dug i trebat će se prelomiti u nekoliko redova. Nadamo se da će lijepo izgledati. + Autor + Ovdje ide suradničko ime autora izabrane slike. + Datum postavljanja + Licencija + Koordinate + Ništa nije navedeno + Postani beta tester + Prijavite se na naš beta-kanal na Google Playu i dobijte raniji pristup novim mogućnostima i ispravkama pogrješaka + Kôd za provjeru u 2 koraka + Moje ograničenje nedavnih postavljanja + Najviše moguće + Nije moguće prikazati više od 500 + Postavi ograničenje nedavnih postavljanja + Kôd za provjeru u 2 koraka nije podržan. + Zaista se želite odjaviti? + Logotip Zajedničkog poslužitelja + Mrežno mjesto Zajedničkog poslužitelja + Stranica Zajedničkog poslužitelja na Facebooku + Izvorni kôd Zajedničkog poslužitelja na Githubu + Pozadinska slika + Slika nije uspjela + Slika nije pronađena + Postavi sliku + Planina Zao + Ljame + Dugin most + Tulipan + Bez selfija + Vlasnička slika + Welcome (Wikipedija) + Dobro došli (autorska prava) + Sidnejska opera + Odustani + Otvori + Zatvori + Početna stranica + Postavljanje + U blizini + O + Postavke + Povratna informacija + Odjava + Upute + Obavijesti + Izabrano + Mjesta u blizini ne mogu biti prikazana bez dopuštenja određivanja lokacije + nema opisa + Stranica datoteke na Zajedničkom poslužitelju + Stavka na Wikidati + Članak na Wikipediji + Pogrješka predmemoriranja slika + Jedinstveni naziv datoteke koji će služiti kao njeno ime. Možete koristiti uobičajeni jezik s razmacima. Ne uključuje datotečni nastavak. + Opišite medij što je više moguće: gdje je napravljen, što prikazuje,... Opišite objekte ili osobe. Napišite informacije koje ne mogu biti lako okrivene, npr. doba dana ako je u pitanju pejzaž. Ako medij prikazuje nešto neobično, molimo objasnite što je neobično. + Slika je pretamna, želite ili je ipak ostaviti? Zajednički poslužitelj je namijenjen slikama od enciklopedijske vrijednosti. + Slika je mutna, želite ili je ipak ostaviti? Zajednički poslužitelj je namijenjen slikama od enciklopedijske vrijednosti. + Daj dopuštenje + Rabi vanjsku pohranu + Spremite slike načinjene kamerom Vašeg uređaja + Prijavite se na Vaš račun + Pošalji zapisnik + Pošalji zapisnik elektroničkom poštom razvijateljima + Preglednik nije pronađen + Pogrješka! URL nije pronađen + Predloži za brisanje + Slika je predložena za brisanje + Pogledaj u pregledniku + Lokacija nepromijenjena. + Lokacija nedostupna. + Potrebno je dopuštenje za popis mjesta u blizini + PRIBAVI UPUTE + PROČITAJ ČLANAK + %1$s, dobro došli na Zajednički poslužitelj! Drago nam je da ste tu. + %1$s Vam je ostavio poruku na Vašoj razgovornoj stranici + Hvala Vam na uređivanju + %1$s Vas je spomenuo na %2$s. + Prebaci prikaz + UPUTE + WIKIDATA + WIKIPEDIJA + ZAJEDNIČKI POSLUŽITELJ + <u>Ocijenite nas</u> + <u>ČPP</u> + Preskoči upute + Internet nije dostupan + Internet je dostupan + Pogrješka dohvaćanja obavijesti + Nema obavijesti + <u>Prevedi</u> + Jezici + Odaberite jezik na koji bi željeli prevoditi + Nastavi + Odustani + Pokušaj ponovo + U redu! + Ovo su mjesta u blizini koja trebaju slike za ilustriranje članaka o njima na Wikipediji + Dodirnite da biste dobili popis ovih mjesta + Možete postaviti sliku s bilo kojeg mjesta u Vašoj galeriji ili kameri + Slike nisu pronađene! + Pogrješka prilikom učitavanja slika. + Postavio: %1$s + Aplikacija za dijeljenje + Prilikom označavanja slike koordinate nisu navedene + Pogrješka prilikom dohvaćanja mjesta u blizini. + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 49763dda8..8005ce63c 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -58,6 +58,7 @@ 이 파일의 제목을 지정해 주십시오 설명 로그인할 수 없습니다 - 네트워크 오류입니다 + 로그인할 수 없습니다 - 사용자 이름과 비밀번호를 확인해 주십시오 실패한 시도가 너무 많습니다. 몇 분 후에 다시 시도하세요. 죄송합니다, 이 사용자는 공용에서 차단되었습니다 2요소 인증 코드를 제공해야 합니다. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 98e7212df..be40b9f8d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -60,6 +60,7 @@ Tytuł Opis Nie można zalogować - błąd sieci + Nie można się zalogować - sprawdź swoją nazwę użytkownika i hasło Zbyt wiele nieudanych prób zalogowania. Spróbuj ponownie za kilka minut. Przepraszamy, ten użytkownik został zablokowany na Commons Wprowadź swój kod dla dwuetapowej autoryzacji. diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 888a453b9..b995ae220 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -58,6 +58,7 @@ Forneça um título para este ficheiro, por favor Descrição Não foi possível iniciar sessão - falha de rede + Não foi possível iniciar sessão - verifique o seu nome de utilizador e a palavra-passe Demasiadas tentativas malsucedidas. Por favor, tente de novo dentro de alguns minutos. Desculpe, este utilizador foi bloqueado no Commons Precisa fornecer o seu código de ativação de dois fatores. @@ -220,7 +221,7 @@ Configurações Comentários Sair - Tutorial + Explicação Notificações Destacadas Os sítios aqui perto não podem ser apresentados sem permissões de localização diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 54c1f86c5..cf6ed0896 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -58,6 +58,7 @@ Будь ласка, вкажіть назву цього файлу Опис Неможливо увійти — збій у мережі + Неможливо увійти — будь ласка перевірте ім\'я користувача та пароль Надто багато невдалих спроб. Будь ласка, спробуйте знову через кілька хвилин. Вибачте, цього користувача було заблоковано на Вікісховищі Ви повинні надати код двофакторної автентифікації. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 8b794d1c5..c57406c80 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -4,6 +4,7 @@ * Kly * LNDDYL * Liuxinyu970226 +* S099001 * Simon Shek * StephDC * Wwycheuk @@ -28,7 +29,7 @@ 請稍候… 登入成功! 登入失敗! - 找不到檔案。請嘗試其它檔案看看。 + 找不到檔案。請試試看其它檔案。 未能核對身分! 開始上傳! 已上傳%1$s! @@ -42,7 +43,7 @@ 正在上載 %1$d 個檔案 正在上載 %1$d 個檔案 - 我的最近上傳 + 我最近的上傳 已佇列 失敗 %1$d%%完成 @@ -109,7 +110,7 @@ 透過提交此圖片,我宣佈這是我個人創作的成品,且不包含受版權保護或自拍內容,並除此之外遵守<a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">維基媒體共享資源方針</a>。 下載 預設授權條款 - 使用先前標題/說明 + 使用先前標題、說明 自動獲取目前位置 若圖片未有地理標記,就以目前位置來作為分類建議。 夜間模式 @@ -154,9 +155,10 @@ 貢獻您的圖片,使維基百科的文章更加生動! 維基百科的圖片,來自維基共享資源。 您的圖片可以幫助教育世界各地的人。 - 避免使用受版權保護的材料,例如從互聯網找來的圖片、海報、書籍封面等 + 避免使用受版權保護的材料,例如從網際網路找來的圖片、海報、書籍封面等 明白了嗎? 是! + 此提示為空,可能無效。請見錯誤報告: https://github.com/commons-app/apps-android-commons/issues/1333 。 分類 載入中… 未選擇 @@ -242,6 +244,7 @@ 錯誤!查無 URL 提名刪除 此圖片已被提名刪除。 + 此提示為空,可能無效。請見錯誤報告: https://github.com/commons-app/apps-android-commons/issues/1333 。 於瀏覽器檢視 位置無法更改。 位置無效。 From ab2e8c5919687e7c5af37f6dbaa3fc4d5cd3901c Mon Sep 17 00:00:00 2001 From: Ujjwal Agrawal Date: Mon, 28 May 2018 15:14:52 +0530 Subject: [PATCH 66/94] Javadocs added --- .../free/nrw/commons/category/CategoryImagesListFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index 18eb5ec48..0c0b95bb2 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -225,6 +225,10 @@ public class CategoryImagesListFragment extends DaggerFragment { return gridView.getAdapter(); } + /** + * This method will be called on back pressed of CategoryImagesActivity. + * It initializes the grid view by setting adapter. +\ */ @Override public void onResume() { gridView.setAdapter(gridAdapter); From 56592b3bcd2c07167aacc6d5b702c54bd3e06d01 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 18:12:17 +1000 Subject: [PATCH 67/94] Fix all compile errors in Zoom.java and ShareActivity.java --- .../nrw/commons/upload/ShareActivity.java | 28 ++++++--- .../upload/{ZoomUtils.java => Zoom.java} | 58 ++++++++++--------- 2 files changed, 52 insertions(+), 34 deletions(-) rename app/src/main/java/fr/free/nrw/commons/upload/{ZoomUtils.java => Zoom.java} (67%) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 14dcc4a7c..9b92966db 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -2,27 +2,23 @@ package fr.free.nrw.commons.upload; import android.Manifest; -import android.annotation.SuppressLint; import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Bitmap; -import android.graphics.BitmapRegionDecoder; import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; import android.support.design.widget.FloatingActionButton; @@ -30,7 +26,6 @@ import android.support.design.widget.Snackbar; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.support.v4.graphics.BitmapCompat; import android.util.Log; import android.view.MenuItem; import android.view.View; @@ -68,7 +63,6 @@ import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; import fr.free.nrw.commons.mwapi.CategoryApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; -import io.reactivex.schedulers.Schedulers; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; @@ -524,7 +518,27 @@ public class ShareActivity e.printStackTrace(); } - ZoomUtils.zoomImageUtil(thumbView, startBounds, input); + Zoom zoomObj = new Zoom(thumbView, startBounds, input, imageuri, this.getContentResolver()); + Bitmap scaledImage = zoomObj.createScaledImage(); + + // Load the high-resolution "zoomed-in" image. + expandedImageView.setImageBitmap(scaledImage); + + float startScale = zoomObj.adjustStartEndBounds(finalBounds, globalOffset); + + // Hide the thumbnail and show the zoomed-in view. When the animation + // begins, it will position the zoomed-in view in the place of the + // thumbnail. + thumbView.setAlpha(0f); + expandedImageView.setVisibility(View.VISIBLE); + zoomOutButton.setVisibility(View.VISIBLE); + zoomInButton.setVisibility(View.GONE); + + // Set the pivot point for SCALE_X and SCALE_Y transformations + // to the top-left corner of the zoomed-in view (the default + // is the center of the view). + expandedImageView.setPivotX(0f); + expandedImageView.setPivotY(0f); // Construct and run the parallel animation of the four translation and // scale properties (X, Y, SCALE_X, and SCALE_Y). diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java similarity index 67% rename from app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java rename to app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index 74605e063..2bb4a0913 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ZoomUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -1,19 +1,38 @@ package fr.free.nrw.commons.upload; +import android.content.ContentResolver; import android.graphics.Bitmap; import android.graphics.BitmapRegionDecoder; +import android.graphics.Point; import android.graphics.Rect; +import android.net.Uri; import android.provider.MediaStore; import android.support.v4.graphics.BitmapCompat; import android.view.View; +import android.widget.FrameLayout; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; -public class ZoomUtils { +public class Zoom { - static void zoomImageUtil(View thumbView, Rect startBounds, InputStream input) { + private View thumbView; + private Rect startBounds; + private InputStream input; + private Uri imageUri; + private ContentResolver contentResolver; + private FrameLayout flContainer; + + public Zoom(View thumbView, Rect startBounds, InputStream input, Uri imageUri, ContentResolver contentResolver) { + this.thumbView = thumbView; + this.startBounds = startBounds; + this.input = input; + this.imageUri = imageUri; + this.contentResolver = contentResolver; + } + + Bitmap createScaledImage() { Bitmap scaled = null; BitmapRegionDecoder decoder = null; @@ -28,24 +47,23 @@ public class ZoomUtils { System.gc(); Runtime rt = Runtime.getRuntime(); long maxMemory = rt.freeMemory(); - bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageuri); - int bitmapByteCount= BitmapCompat.getAllocationByteCount(bitmap); + bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri); + int bitmapByteCount = BitmapCompat.getAllocationByteCount(bitmap); long height = bitmap.getHeight(); long width = bitmap.getWidth(); - long calHeight = (long) ((height * maxMemory)/(bitmapByteCount * 1.1)); - long calWidth = (long) ((width * maxMemory)/(bitmapByteCount * 1.1)); - scaled = Bitmap.createScaledBitmap(bitmap,(int) Math.min(width,calWidth), (int) Math.min(height,calHeight), true); + long calHeight = (long) ((height * maxMemory) / (bitmapByteCount * 1.1)); + long calWidth = (long) ((width * maxMemory) / (bitmapByteCount * 1.1)); + scaled = Bitmap.createScaledBitmap(bitmap, (int) Math.min(width, calWidth), (int) Math.min(height, calHeight), true); } catch (IOException e) { - } catch (NullPointerException e){ + } catch (NullPointerException e) { scaled = bitmap; } - // Load the high-resolution "zoomed-in" image. - expandedImageView.setImageBitmap(scaled); - + return scaled; + } + float adjustStartEndBounds(Rect finalBounds, Point globalOffset) { // Calculate the starting and ending bounds for the zoomed-in image. - // This step involves lots of math. Yay, math. // The start bounds are the global visible rectangle of the thumbnail, // and the final bounds are the global visible rectangle of the container // view. Also set the container view's offset as the origin for the @@ -77,20 +95,6 @@ public class ZoomUtils { startBounds.top -= deltaHeight; startBounds.bottom += deltaHeight; } - - // Hide the thumbnail and show the zoomed-in view. When the animation - // begins, it will position the zoomed-in view in the place of the - // thumbnail. - thumbView.setAlpha(0f); - expandedImageView.setVisibility(View.VISIBLE); - zoomOutButton.setVisibility(View.VISIBLE); - zoomInButton.setVisibility(View.GONE); - - // Set the pivot point for SCALE_X and SCALE_Y transformations - // to the top-left corner of the zoomed-in view (the default - // is the center of the view). - expandedImageView.setPivotX(0f); - expandedImageView.setPivotY(0f); - + return startScale; } } From 50b674f2211d36e942c43b96d6a1a5a97e6f1c19 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 18:17:27 +1000 Subject: [PATCH 68/94] Rearrange member variables and method parameters in Zoom.java --- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 6 +++--- app/src/main/java/fr/free/nrw/commons/upload/Zoom.java | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 9b92966db..452c9b3d2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -501,7 +501,7 @@ public class ShareActivity /* * function to provide pinch zoom */ - private void zoomImageFromThumb(final View thumbView, Uri imageuri ) { + private void zoomImageFromThumb(final View thumbView, Uri imageuri) { // If there's an animation in progress, cancel it immediately and proceed with this one. if (CurrentAnimator != null) { CurrentAnimator.cancel(); @@ -518,13 +518,13 @@ public class ShareActivity e.printStackTrace(); } - Zoom zoomObj = new Zoom(thumbView, startBounds, input, imageuri, this.getContentResolver()); + Zoom zoomObj = new Zoom(thumbView, flContainer, input, imageuri, this.getContentResolver()); Bitmap scaledImage = zoomObj.createScaledImage(); // Load the high-resolution "zoomed-in" image. expandedImageView.setImageBitmap(scaledImage); - float startScale = zoomObj.adjustStartEndBounds(finalBounds, globalOffset); + float startScale = zoomObj.adjustStartEndBounds(startBounds, finalBounds, globalOffset); // Hide the thumbnail and show the zoomed-in view. When the animation // begins, it will position the zoomed-in view in the place of the diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index 2bb4a0913..ebfca14f3 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -18,18 +18,18 @@ import java.io.InputStream; public class Zoom { private View thumbView; - private Rect startBounds; private InputStream input; private Uri imageUri; private ContentResolver contentResolver; private FrameLayout flContainer; - public Zoom(View thumbView, Rect startBounds, InputStream input, Uri imageUri, ContentResolver contentResolver) { + + Zoom(View thumbView, FrameLayout flContainer, InputStream input, Uri imageUri, ContentResolver contentResolver) { this.thumbView = thumbView; - this.startBounds = startBounds; this.input = input; this.imageUri = imageUri; this.contentResolver = contentResolver; + this.flContainer = flContainer; } Bitmap createScaledImage() { @@ -61,8 +61,7 @@ public class Zoom { return scaled; } - - float adjustStartEndBounds(Rect finalBounds, Point globalOffset) { + float adjustStartEndBounds(Rect startBounds, Rect finalBounds, Point globalOffset) { // Calculate the starting and ending bounds for the zoomed-in image. // The start bounds are the global visible rectangle of the thumbnail, // and the final bounds are the global visible rectangle of the container From 0d282af0e6585657f7ccb0e173db52065cac767c Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 18:30:17 +1000 Subject: [PATCH 69/94] Catch exceptions in Zoom.java --- .../main/java/fr/free/nrw/commons/upload/Zoom.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index ebfca14f3..bda3c2ad5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -15,6 +15,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import timber.log.Timber; + public class Zoom { private View thumbView; @@ -23,7 +25,6 @@ public class Zoom { private ContentResolver contentResolver; private FrameLayout flContainer; - Zoom(View thumbView, FrameLayout flContainer, InputStream input, Uri imageUri, ContentResolver contentResolver) { this.thumbView = thumbView; this.input = input; @@ -36,12 +37,16 @@ public class Zoom { Bitmap scaled = null; BitmapRegionDecoder decoder = null; + Bitmap bitmap = null; + try { decoder = BitmapRegionDecoder.newInstance(input, false); + bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null); } catch (IOException e) { - e.printStackTrace(); + Timber.e(e); + } catch (NullPointerException e) { + Timber.e(e); } - Bitmap bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null); try { //Compress the Image System.gc(); @@ -55,7 +60,9 @@ public class Zoom { long calWidth = (long) ((width * maxMemory) / (bitmapByteCount * 1.1)); scaled = Bitmap.createScaledBitmap(bitmap, (int) Math.min(width, calWidth), (int) Math.min(height, calHeight), true); } catch (IOException e) { + Timber.e(e); } catch (NullPointerException e) { + Timber.e(e); scaled = bitmap; } return scaled; From 7dcf2376a6fc83cd40e4eef0eec4ec56596ac0d5 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 18:36:37 +1000 Subject: [PATCH 70/94] Rearrange member vars and method params in Zoom.java --- .../free/nrw/commons/upload/ShareActivity.java | 17 +++++++---------- .../java/fr/free/nrw/commons/upload/Zoom.java | 8 ++------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 452c9b3d2..d405c095f 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -497,9 +497,8 @@ public class ShareActivity return super.onOptionsItemSelected(item); } - //TODO: Move this to a new class. Save references to the findViewByIds and pass them to the new method - /* - * function to provide pinch zoom + /** + * Allows zooming in to the uploaded image. Called when zoom FAB is tapped */ private void zoomImageFromThumb(final View thumbView, Uri imageuri) { // If there's an animation in progress, cancel it immediately and proceed with this one. @@ -511,15 +510,14 @@ public class ShareActivity mainFab.setVisibility(View.GONE); InputStream input = null; - try { input = this.getContentResolver().openInputStream(imageuri); } catch (FileNotFoundException e) { e.printStackTrace(); } - Zoom zoomObj = new Zoom(thumbView, flContainer, input, imageuri, this.getContentResolver()); - Bitmap scaledImage = zoomObj.createScaledImage(); + Zoom zoomObj = new Zoom(thumbView, flContainer, this.getContentResolver()); + Bitmap scaledImage = zoomObj.createScaledImage(input, imageuri); // Load the high-resolution "zoomed-in" image. expandedImageView.setImageBitmap(scaledImage); @@ -569,8 +567,8 @@ public class ShareActivity startScaleFinal = startScale; } - /* - * called when upper arrow floating button + /** + * Called when user taps the ^ FAB button, expands to show Zoom and Map */ @OnClick(R.id.main_fab) public void onMainFabClicked() { @@ -583,11 +581,10 @@ public class ShareActivity @OnClick(R.id.media_upload_zoom_in) public void onZoomInFabClicked() { - //This try catch block was originally holding the entire click listener on the fab button, I did not wanted to risk exceptions try { zoomImageFromThumb(backgroundImageView, mediaUri); } catch (Exception e) { - Log.i("exception", e.toString()); + Timber.e(e); } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index bda3c2ad5..4bcd23a53 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -20,20 +20,16 @@ import timber.log.Timber; public class Zoom { private View thumbView; - private InputStream input; - private Uri imageUri; private ContentResolver contentResolver; private FrameLayout flContainer; - Zoom(View thumbView, FrameLayout flContainer, InputStream input, Uri imageUri, ContentResolver contentResolver) { + Zoom(View thumbView, FrameLayout flContainer, ContentResolver contentResolver) { this.thumbView = thumbView; - this.input = input; - this.imageUri = imageUri; this.contentResolver = contentResolver; this.flContainer = flContainer; } - Bitmap createScaledImage() { + Bitmap createScaledImage(InputStream input, Uri imageUri) { Bitmap scaled = null; BitmapRegionDecoder decoder = null; From 96ea3f693d2f2312c676d4c69a83b060501255d0 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 18:41:47 +1000 Subject: [PATCH 71/94] Fix lint issues --- app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index d405c095f..2ddc0133d 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -628,7 +628,7 @@ public class ShareActivity @OnClick(R.id.media_map) public void onFabShowMapsClicked() { - if (imageObj != null && imageObj.imageCoordsExists == true) { + if (imageObj != null && imageObj.imageCoordsExists) { Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); From fe185e0da83718a9251d2db46048a3170e2b294d Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:02:38 +1000 Subject: [PATCH 72/94] Rename mapsFragment to mapButton to be consistent with other FABs --- .../fr/free/nrw/commons/upload/ShareActivity.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 2ddc0133d..6fec6c3d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -26,7 +26,6 @@ import android.support.design.widget.Snackbar; import android.support.graphics.drawable.VectorDrawableCompat; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; -import android.util.Log; import android.view.MenuItem; import android.view.View; import android.view.animation.DecelerateInterpolator; @@ -110,7 +109,7 @@ public class ShareActivity @BindView(R.id.backgroundImage) SimpleDraweeView backgroundImageView; @BindView(R.id.media_map) - FloatingActionButton mapsFragment; + FloatingActionButton mapButton; @BindView(R.id.media_upload_zoom_in) FloatingActionButton zoomInButton; @BindView(R.id.media_upload_zoom_out) @@ -343,11 +342,11 @@ public class ShareActivity isFABOpen=true; if( imageObj != null && imageObj.imageCoordsExists) - mapsFragment.setVisibility(View.VISIBLE); + mapButton.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); mainFab.animate().rotationBy(180); - mapsFragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); + mapButton.animate().translationY(-getResources().getDimension(R.dimen.second_fab)); zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab)); } @@ -357,7 +356,7 @@ public class ShareActivity private void closeFABMenu(){ isFABOpen=false; mainFab.animate().rotationBy(-180); - mapsFragment.animate().translationY(0); + mapButton.animate().translationY(0); zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animator) { @@ -366,7 +365,7 @@ public class ShareActivity @Override public void onAnimationEnd(Animator animator) { if(!isFABOpen){ - mapsFragment.setVisibility(View.GONE); + mapButton.setVisibility(View.GONE); zoomInButton.setVisibility(View.GONE); } } @@ -498,7 +497,7 @@ public class ShareActivity } /** - * Allows zooming in to the uploaded image. Called when zoom FAB is tapped + * Allows zooming in to the image about to be uploaded. Called when zoom FAB is tapped */ private void zoomImageFromThumb(final View thumbView, Uri imageuri) { // If there's an animation in progress, cancel it immediately and proceed with this one. From 82659316ff963bacad69a1681d05d60208e0a075 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:07:53 +1000 Subject: [PATCH 73/94] Add Javadocs to Zoom.java --- .../java/fr/free/nrw/commons/upload/Zoom.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index 4bcd23a53..44eba9f2b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -17,6 +17,9 @@ import java.io.InputStream; import timber.log.Timber; +/** + * Contains utility methods for the Zoom function in ShareActivity. + */ public class Zoom { private View thumbView; @@ -29,6 +32,12 @@ public class Zoom { this.flContainer = flContainer; } + /** + * Create a scaled bitmap to display the zoomed-in image + * @param input the input stream corresponding to the uploaded image + * @param imageUri the uploaded image's URI + * @return a zoomable bitmap + */ Bitmap createScaledImage(InputStream input, Uri imageUri) { Bitmap scaled = null; @@ -64,13 +73,18 @@ public class Zoom { return scaled; } + /** + * Calculate the starting and ending bounds for the zoomed-in image. + * Also set the container view's offset as the origin for the + * bounds, since that's the origin for the positioning animation + * properties (X, Y). + * @param startBounds the global visible rectangle of the thumbnail + * @param finalBounds the global visible rectangle of the container view + * @param globalOffset the container view's offset + * @return scaled start bounds + */ float adjustStartEndBounds(Rect startBounds, Rect finalBounds, Point globalOffset) { - // Calculate the starting and ending bounds for the zoomed-in image. - // The start bounds are the global visible rectangle of the thumbnail, - // and the final bounds are the global visible rectangle of the container - // view. Also set the container view's offset as the origin for the - // bounds, since that's the origin for the positioning animation - // properties (X, Y). + thumbView.getGlobalVisibleRect(startBounds); flContainer.getGlobalVisibleRect(finalBounds, globalOffset); startBounds.offset(-globalOffset.x, -globalOffset.y); From c3e911f13501cef079e01c0c69eb5be82128b95d Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:12:45 +1000 Subject: [PATCH 74/94] Inject prefs into FileProcessor instead of passing it from ShareActivity --- .../java/fr/free/nrw/commons/upload/FileProcessor.java | 8 +++++--- .../java/fr/free/nrw/commons/upload/ShareActivity.java | 7 +++---- app/src/main/java/fr/free/nrw/commons/upload/Zoom.java | 1 - 3 files changed, 8 insertions(+), 8 deletions(-) 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 ece18f130..71f5e07b2 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 @@ -20,6 +20,7 @@ import java.util.Date; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.di.ApplicationlessInjection; @@ -34,7 +35,6 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ private Uri mediaUri; private ContentResolver contentResolver; private GPSExtractor imageObj; - private SharedPreferences prefs; private Context context; private String decimalCoords; private boolean haveCheckedForOtherImages = false; @@ -49,11 +49,13 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ GpsCategoryModel gpsCategoryModel; @Inject CategoryApi apiCall; + @Inject + @Named("default_preferences") + SharedPreferences prefs; - FileProcessor(Uri mediaUri, ContentResolver contentResolver, SharedPreferences prefs, Context context) { + FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) { this.mediaUri = mediaUri; this.contentResolver = contentResolver; - this.prefs = prefs; this.context = context; useExtStorage = prefs.getBoolean("useExternalStorage", true); ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 6fec6c3d0..9b54daa68 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -1,12 +1,11 @@ package fr.free.nrw.commons.upload; import android.Manifest; - -import android.app.Activity; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -305,7 +304,7 @@ public class ShareActivity uploadController.prepareService(); ContentResolver contentResolver = this.getContentResolver(); - fileObj = new FileProcessor(mediaUri, contentResolver, prefs, this); + fileObj = new FileProcessor(mediaUri, contentResolver, this); checkIfFileExists(); fileObj.getFileCoordinates(locationPermitted); } @@ -342,7 +341,7 @@ public class ShareActivity isFABOpen=true; if( imageObj != null && imageObj.imageCoordsExists) - mapButton.setVisibility(View.VISIBLE); + mapButton.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); mainFab.animate().rotationBy(180); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java index 44eba9f2b..438c7f77b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java @@ -11,7 +11,6 @@ import android.support.v4.graphics.BitmapCompat; import android.view.View; import android.widget.FrameLayout; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; From a082f509e7a2542b43afbbfb7082353c319d7027 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:15:14 +1000 Subject: [PATCH 75/94] Add Javadocs --- .../java/fr/free/nrw/commons/upload/FileProcessor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 71f5e07b2..2d14b70ce 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 @@ -30,6 +30,9 @@ import timber.log.Timber; import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; +/** + * Processing of the image file that is about to be uploaded via ShareActivity is done here + */ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ private Uri mediaUri; @@ -126,6 +129,10 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ } } + /** + * Find other images around the same location that were taken within the last 20 sec + * @param gpsEnabled True if GPS is enabled + */ private void findOtherImages(boolean gpsEnabled) { Timber.d("filePath"+getPathOfMediaOrCopy()); @@ -175,7 +182,6 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ haveCheckedForOtherImages = true; //Finished checking for other images } - /** * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates. * Then initiates the calls to MediaWiki API through an instance of CategoryApi. From dac6a15e60a36474cfa6798de7acc45bc0e17254 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:16:52 +1000 Subject: [PATCH 76/94] Pass the GPSExtractor created in FileProcessor.java back into ShareActivity --- .../main/java/fr/free/nrw/commons/upload/FileProcessor.java | 3 ++- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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 2d14b70ce..0f83b5026 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 @@ -99,7 +99,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ * Gets coordinates for category suggestions, either from EXIF data or user location * @param gpsEnabled if true use GPS */ - void getFileCoordinates(boolean gpsEnabled) { + GPSExtractor getFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); @@ -127,6 +127,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ } catch (FileNotFoundException e) { Timber.w("File not found: " + mediaUri, e); } + return imageObj; } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 9b54daa68..ffb33f545 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -306,7 +306,7 @@ public class ShareActivity ContentResolver contentResolver = this.getContentResolver(); fileObj = new FileProcessor(mediaUri, contentResolver, this); checkIfFileExists(); - fileObj.getFileCoordinates(locationPermitted); + imageObj = fileObj.getFileCoordinates(locationPermitted); } /** From 15b6a5e16e0e6a392c5d6defbfdd97847783afc8 Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:20:47 +1000 Subject: [PATCH 77/94] Only use prefs after doing ApplicationlessInjection --- app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0f83b5026..b9d03ee49 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 @@ -60,8 +60,8 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ this.mediaUri = mediaUri; this.contentResolver = contentResolver; this.context = context; - useExtStorage = prefs.getBoolean("useExternalStorage", true); ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this); + useExtStorage = prefs.getBoolean("useExternalStorage", true); } /** From f28c5c7a5f62db53cbcc9c828b7dc5234c631a6c Mon Sep 17 00:00:00 2001 From: misaochan Date: Tue, 29 May 2018 19:26:34 +1000 Subject: [PATCH 78/94] Add TODO --- app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java | 2 ++ 1 file changed, 2 insertions(+) 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 b9d03ee49..88e1c426d 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 @@ -95,6 +95,8 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ return filePath; } + //TODO: Figure out why coords are not sent to location template, use LocationServiceManager + /** * Gets coordinates for category suggestions, either from EXIF data or user location * @param gpsEnabled if true use GPS From bcbf0db1ff81c47d893823409ee04c08446f546f Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Wed, 30 May 2018 15:41:36 +0530 Subject: [PATCH 79/94] Add option to set image as wallpaper (#1535) * Add option to set image as wallpaper * Added java docs * Toast message on setting the wallpaper successfully --- app/src/main/AndroidManifest.xml | 1 + .../media/MediaDetailPagerFragment.java | 24 +++++++ .../fr/free/nrw/commons/utils/ImageUtils.java | 70 +++++++++++++++++++ .../main/res/menu/fragment_image_detail.xml | 4 ++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 101 insertions(+) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 17f6770d2..45a647227 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index 62d1261cf..2c58e8dbb 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -2,9 +2,11 @@ package fr.free.nrw.commons.media; import android.annotation.SuppressLint; import android.app.DownloadManager; +import android.app.WallpaperManager; import android.content.Intent; import android.content.SharedPreferences; import android.database.DataSetObserver; +import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -26,6 +28,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import java.io.IOException; + import butterknife.BindView; import butterknife.ButterKnife; import javax.inject.Inject; @@ -38,12 +42,15 @@ import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.ImageUtils; +import timber.log.Timber; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.content.Context.DOWNLOAD_SERVICE; import static android.content.Intent.ACTION_VIEW; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.widget.Toast.LENGTH_SHORT; +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment implements ViewPager.OnPageChangeListener { @@ -140,6 +147,10 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple // Download downloadMedia(m); return true; + case R.id.menu_set_as_wallpaper: + // Set wallpaper + setWallpaper(m); + return true; case R.id.menu_retry_current_image: // Retry ((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem()); @@ -155,6 +166,19 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple } } + /** + * Set the media as the device's wallpaper if the imageUrl is not null + * Fails silently if setting the wallpaper fails + * @param media + */ + private void setWallpaper(Media media) { + if(media.getImageUrl() == null || media.getImageUrl().isEmpty()) { + Timber.d("Media URL not present"); + return; + } + ImageUtils.setWallpaperFromImageUrl(getActivity(), Uri.parse(media.getImageUrl())); + } + /** * Start the media file downloading to the local SD card/storage. * The file can then be opened in Gallery or other apps. diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java index 4f6a6d456..a091d7758 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java @@ -1,12 +1,34 @@ package fr.free.nrw.commons.utils; +import android.app.WallpaperManager; +import android.content.Context; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.graphics.BitmapRegionDecoder; import android.graphics.Color; import android.graphics.Rect; +import android.net.Uri; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import com.facebook.common.executors.CallerThreadExecutor; +import com.facebook.common.references.CloseableReference; +import com.facebook.datasource.DataSource; +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.imagepipeline.core.ImagePipeline; +import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; +import com.facebook.imagepipeline.image.CloseableImage; +import com.facebook.imagepipeline.request.ImageRequest; +import com.facebook.imagepipeline.request.ImageRequestBuilder; + +import java.io.IOException; + +import fr.free.nrw.commons.R; import timber.log.Timber; +import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; + /** * Created by bluesir9 on 3/10/17. */ @@ -132,4 +154,52 @@ public class ImageUtils { return isImageDark; } + + /** + * Downloads the image from the URL and sets it as the phone's wallpaper + * Fails silently if download or setting wallpaper fails. + * @param context + * @param imageUrl + */ + public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) { + Timber.d("Trying to set wallpaper from url %s", imageUrl.toString()); + ImageRequest imageRequest = ImageRequestBuilder + .newBuilderWithSource(imageUrl) + .setAutoRotateEnabled(true) + .build(); + + ImagePipeline imagePipeline = Fresco.getImagePipeline(); + final DataSource> + dataSource = imagePipeline.fetchDecodedImage(imageRequest, context); + + dataSource.subscribe(new BaseBitmapDataSubscriber() { + + @Override + public void onNewResultImpl(@Nullable Bitmap bitmap) { + if (dataSource.isFinished() && bitmap != null){ + Timber.d("Bitmap loaded from url %s", imageUrl.toString()); + setWallpaper(context, Bitmap.createBitmap(bitmap)); + dataSource.close(); + } + } + + @Override + public void onFailureImpl(DataSource dataSource) { + Timber.d("Error getting bitmap from image url %s", imageUrl.toString()); + if (dataSource != null) { + dataSource.close(); + } + } + }, CallerThreadExecutor.getInstance()); + } + + private static void setWallpaper(Context context, Bitmap bitmap) { + WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); + try { + wallpaperManager.setBitmap(bitmap); + ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully)); + } catch (IOException e) { + Timber.e(e,"Error setting wallpaper"); + } + } } diff --git a/app/src/main/res/menu/fragment_image_detail.xml b/app/src/main/res/menu/fragment_image_detail.xml index e864dddb2..70a35951a 100644 --- a/app/src/main/res/menu/fragment_image_detail.xml +++ b/app/src/main/res/menu/fragment_image_detail.xml @@ -15,6 +15,10 @@ android:id="@+id/menu_download_current_image" android:title="@string/menu_download" app:showAsAction="never" /> + Coordinates were not specified during image selection Error fetching nearby places. + Set wallpaper + Wallpaper set successfully! From 4815e93fc998bf88794d21d1e05a5480d9c2d91b Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Thu, 31 May 2018 06:30:17 +0530 Subject: [PATCH 80/94] Make P18 edits to corresponding wikidata entity on uploading from Nearby (#1495) * Localisation updates from https://translatewiki.net. * Integrate API for displaying featured images (#1456) * Integrate API for displaying featured images * Add pagination and refactor code so that it can be reused for category images * Add license info to the images * Fix author view * Remove unused values * Fix minor issues with featured images * Fix null license url issue * Remove some log lines * Fix back navigation issue * fix tests * fix test inits * Gracefully handling various error situations * Added java docs * Update pull_request_template.md (#1476) * Update pull_request_template.md * Remove Javadocs mention * Added required/optional notes * resolves #1464 : MediaDataExtractor is making inefficient (redundant) server calls (#1496) * Open map of place where picture was taken (#1360) * Intent to map added * Merge conflicts resolved * Added the functionality to hide FAB incase of null coordinate * Merge Conflict resolved * Improve pr quality * Improve Quality * Added nested FAB animations * Nested FAB implemented * Improve Quality * Added up arrow * Javadocs Added * Add nearby tutorial (#1467) * Add dependency for MaterialShowcase * Add actionview class to get a reference to material showcase * Create a NearbyMaterialShowcaseSequence class * Apply sequence steps * Add first three steps of nearby showcase * Add sequence id constants to make sure they will be displayed only once * Add last step of sequence to explain plus fab * Create an object to prevent customize all sequences every time * Fix typo * Code cleanup * Add strings to strings.xml * Code cleanup * Revert irrelevant change * Revert irrelevant change * Remove showcaseview for recenter button * Use single showcaseView instead of sequence * Add single showcase view insted of sequence to be able to edit text style * Make sure it will be displayed only once * Cleanup * Update strings * Change dismiss text style * CONTRIBUTING: fix formatting of the gist of the guidelines (#1453) * CONTRIBUTING: fix formatting of the gist of the guidelines First level headings for a gist seems to be overkill. So, replace first level headings with an ordered-list which sounds more meaningful. * CONTRIBUTING: specify clearly that 'blame' is a feature of "Git" The contributing file specifies about the ability to know who wrote something without the need of @author javadoc tags but incorrectly attributes the feature to GitHub. Correctly attribute the feature to where it belongs, Git, and specify the name of the feature to help users easily take advantage of it. * Feature/switch to butterknife (#1494) * Implemented butterknife in MediaDetailFragment [issue #1491] * Implemented butterknife in MediaDetailPagerFragment [[issue #1491]] * post merge upstream master wip [[issue #1491]] * Localisation updates from https://translatewiki.net. * Bug fix #1504 (#1506) * Bug fix #1504 * Filtered messages with ConnectException [issue #1504] * A generalised message for exceptions in Nearby Activity [issue #1504] * Localisation updates from https://translatewiki.net. * Fix security exception crash while accessing network location provider (#1498) * Fix security exception crash while accessing network location provider * Added java docs * Localisation updates from https://translatewiki.net. * Log P18 edits to wikidata corresponding wikidata entity on uploading a nearby image * Added java docs * Fix test build * Refresh nearby * Refresh nearby list on successful edit * Java docs * Make authenticated wikidata edits * Updated toast message to show entity name that was edited --- app/build.gradle | 2 + .../main/java/fr/free/nrw/commons/Utils.java | 1 + .../commons/contributions/Contribution.java | 14 ++++ .../contributions/ContributionController.java | 12 ++- .../ContributionsListFragment.java | 2 +- .../commons/di/CommonsApplicationModule.java | 10 ++- .../location/LocationServiceManager.java | 3 +- .../mwapi/ApacheHttpClientMediaWikiApi.java | 74 +++++++++++++++++++ .../free/nrw/commons/mwapi/MediaWikiApi.java | 7 ++ .../nrw/commons/nearby/NearbyActivity.java | 45 +++++++---- .../commons/nearby/NearbyListFragment.java | 11 ++- .../nrw/commons/nearby/NearbyMapFragment.java | 3 +- .../fr/free/nrw/commons/nearby/Place.java | 15 ++++ .../notification/NotificationActivity.java | 5 +- .../nrw/commons/upload/ShareActivity.java | 13 ++-- .../nrw/commons/upload/UploadController.java | 3 +- .../nrw/commons/upload/UploadService.java | 52 +++++++++++++ .../wikidata/WikidataEditListener.java | 16 ++++ .../wikidata/WikidataEditListenerImpl.java | 20 +++++ app/src/main/res/values/strings.xml | 2 + .../mwapi/ApacheHttpClientMediaWikiApiTest.kt | 4 +- 21 files changed, 276 insertions(+), 38 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java create mode 100644 app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java diff --git a/app/build.gradle b/app/build.gradle index 5d37f8f54..72dbd7546 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -130,6 +130,7 @@ android { productFlavors { prod { buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\"" + buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\"" buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\"" buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.wikimedia.org/wikipedia/commons\"" buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\"" @@ -146,6 +147,7 @@ android { beta { // What values do we need to hit the BETA versions of the site / api ? buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\"" + buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\"" buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\"" buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.beta.wmflabs.org/wikipedia/commons\"" buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\"" diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java index 91c23ce26..9f89586c0 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -178,6 +178,7 @@ public class Utils { } public static void handleWebUrl(Context context, Uri url) { + Timber.d("Launching web url %s", url.toString()); Intent browserIntent = new Intent(Intent.ACTION_VIEW, url); if (browserIntent.resolveActivity(context.getPackageManager()) == null) { Toast toast = Toast.makeText(context, context.getString(R.string.no_web_browser), LENGTH_SHORT); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java index 7861f96de..99009c029 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java @@ -45,6 +45,7 @@ public class Contribution extends Media { private long transferred; private String decimalCoords; private boolean isMultiple; + private String wikiDataEntityId; public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp, int state, long dataLength, Date dateUploaded, long transferred, @@ -222,4 +223,17 @@ public class Contribution extends Media { throw new RuntimeException("Unrecognized license value: " + license); } + + public String getWikiDataEntityId() { + return wikiDataEntityId; + } + + /** + * When the corresponding wikidata entity is known as in case of nearby uploads, it can be set + * using the setter method + * @param wikiDataEntityId + */ + public void setWikiDataEntityId(String wikiDataEntityId) { + this.wikiDataEntityId = wikiDataEntityId; + } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java index 37b3d5377..ed6001f94 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java @@ -90,7 +90,7 @@ public class ContributionController { fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY); } - public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload) { + public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload, String wikiDataEntityId) { FragmentActivity activity = fragment.getActivity(); Timber.d("handleImagePicked() called with onActivityResult()"); Intent shareIntent = new Intent(activity, ShareActivity.class); @@ -102,9 +102,6 @@ public class ContributionController { shareIntent.setType(activity.getContentResolver().getType(imageData)); shareIntent.putExtra(EXTRA_STREAM, imageData); shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY); - if (isDirectUpload) { - shareIntent.putExtra("isDirectUpload", true); - } break; case SELECT_FROM_CAMERA: //FIXME: Find out appropriate mime type @@ -113,9 +110,6 @@ public class ContributionController { shareIntent.setType("image/jpeg"); shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri); shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA); - if (isDirectUpload) { - shareIntent.putExtra("isDirectUpload", true); - } break; default: @@ -123,6 +117,10 @@ public class ContributionController { } Timber.i("Image selected"); try { + shareIntent.putExtra("isDirectUpload", isDirectUpload); + if (wikiDataEntityId != null && !wikiDataEntityId.equals("")) { + shareIntent.putExtra("wikiDataEntityId", wikiDataEntityId); + } activity.startActivity(shareIntent); } catch (SecurityException e) { Timber.e(e, "Security Exception"); diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java index ff400a8dd..0b600c5d0 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java @@ -117,7 +117,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { if (resultCode == RESULT_OK) { Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); - controller.handleImagePicked(requestCode, data, false); + controller.handleImagePicked(requestCode, data, false, null); } else { Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); 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 55281be7e..1c54366af 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 @@ -24,6 +24,8 @@ import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; +import fr.free.nrw.commons.wikidata.WikidataEditListener; +import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl; import static android.content.Context.MODE_PRIVATE; import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY; @@ -123,7 +125,7 @@ public class CommonsApplicationModule { @Named("default_preferences") SharedPreferences defaultPreferences, @Named("category_prefs") SharedPreferences categoryPrefs, Gson gson) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson); } @Provides @@ -165,4 +167,10 @@ public class CommonsApplicationModule { public LruCache provideLruCache() { return new LruCache<>(1024); } + + @Provides + @Singleton + public WikidataEditListener provideWikidataEditListener() { + return new WikidataEditListenerImpl(); + } } \ No newline at end of file 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 49c422633..cd1082ba5 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 @@ -284,6 +284,7 @@ public class LocationServiceManager implements LocationListener { LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving LOCATION_NOT_CHANGED, - PERMISSION_JUST_GRANTED + PERMISSION_JUST_GRANTED, + MAP_UPDATED } } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 6629d0933..47b9b5925 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -25,6 +25,8 @@ import org.apache.http.params.CoreProtocolPNames; import org.apache.http.util.EntityUtils; import org.mediawiki.api.ApiResult; import org.mediawiki.api.MWApi; +import org.w3c.dom.Element; +import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.IOException; @@ -62,6 +64,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { private static final String THUMB_SIZE = "640"; private AbstractHttpClient httpClient; private MWApi api; + private MWApi wikidataApi; private Context context; private SharedPreferences defaultPreferences; private SharedPreferences categoryPreferences; @@ -69,6 +72,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { public ApacheHttpClientMediaWikiApi(Context context, String apiURL, + String wikidatApiURL, SharedPreferences defaultPreferences, SharedPreferences categoryPreferences, Gson gson) { @@ -82,6 +86,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent()); httpClient = new DefaultHttpClient(cm, params); api = new MWApi(apiURL, httpClient); + wikidataApi = new MWApi(wikidatApiURL, httpClient); this.defaultPreferences = defaultPreferences; this.categoryPreferences = categoryPreferences; this.gson = gson; @@ -206,6 +211,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { return api.getEditToken(); } + @Override + public String getCentralAuthToken() throws IOException { + String centralAuthToken = api.action("centralauthtoken") + .get() + .getString("/api/centralauthtoken/@centralauthtoken"); + Timber.d("MediaWiki Central auth token is %s", centralAuthToken); + return centralAuthToken; + } + @Override public boolean fileExistsWithName(String fileName) throws IOException { return api.action("query") @@ -351,6 +365,65 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { }).flatMapObservable(Observable::fromIterable); } + /** + * Get the edit token for making wiki data edits + * https://www.mediawiki.org/wiki/API:Tokens + * @return + * @throws IOException + */ + private String getWikidataEditToken() throws IOException { + return wikidataApi.getEditToken(); + } + + @Override + public String getWikidataCsrfToken() throws IOException { + String wikidataCsrfToken = wikidataApi.action("query") + .param("action", "query") + .param("centralauthtoken", getCentralAuthToken()) + .param("meta", "tokens") + .post() + .getString("/api/query/tokens/@csrftoken"); + Timber.d("Wikidata csrf token is %s", wikidataCsrfToken); + return wikidataCsrfToken; + } + + /** + * Creates a new claim using the wikidata API + * https://www.mediawiki.org/wiki/Wikibase/API + * @param entityId the wikidata entity to be edited + * @param property the property to be edited, for eg P18 for images + * @param snaktype the type of value stored for that property + * @param value the actual value to be stored for the property, for eg filename in case of P18 + * @return returns true if the claim is successfully created + * @throws IOException + */ + @Nullable + @Override + public boolean wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException { + ApiResult result = wikidataApi.action("wbcreateclaim") + .param("entity", entityId) + .param("centralauthtoken", getCentralAuthToken()) + .param("token", getWikidataCsrfToken()) + .param("snaktype", snaktype) + .param("property", property) + .param("value", value) + .post(); + + if (result == null || result.getNode("api") == null) { + return false; + } + + Node node = result.getNode("api").getDocument(); + Element element = (Element) node; + + if (element != null && element.getAttribute("success").equals("1")) { + return true; + } else { + Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info")); + } + return false; + } + @Override @NonNull public Observable searchTitles(String title, int searchCatsLimit) { @@ -586,6 +659,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { String resultStatus = result.getString("/api/upload/@result"); if (!resultStatus.equals("Success")) { String errorCode = result.getString("/api/error/@code"); + Timber.e(errorCode); return new UploadResult(resultStatus, errorCode); } else { Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp")); diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index c0bd2fd87..869205f79 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -27,6 +27,10 @@ public interface MediaWikiApi { String getEditToken() throws IOException; + String getWikidataCsrfToken() throws IOException; + + String getCentralAuthToken() throws IOException; + boolean fileExistsWithName(String fileName) throws IOException; boolean pageExists(String pageName) throws IOException; @@ -49,6 +53,9 @@ public interface MediaWikiApi { @Nullable String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException; + @Nullable + boolean wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException; + @NonNull MediaResult fetchMediaByFilename(String filename) throws IOException; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java index 35e15b0d9..df31a8761 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java @@ -42,11 +42,13 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationServiceManager; +import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType; import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.UriSerializer; import fr.free.nrw.commons.utils.ViewUtil; +import fr.free.nrw.commons.wikidata.WikidataEditListener; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -55,8 +57,12 @@ import timber.log.Timber; import uk.co.deanwild.materialshowcaseview.IShowcaseListener; import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView; +import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.*; +import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; -public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener { + +public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener, + WikidataEditListener.WikidataP18EditListener { private static final int LOCATION_REQUEST = 1; @@ -76,6 +82,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp LocationServiceManager locationManager; @Inject NearbyController nearbyController; + @Inject WikidataEditListener wikidataEditListener; + @Inject @Named("application_preferences") SharedPreferences applicationPrefs; private LatLng curLatLng; @@ -110,6 +118,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp initBottomSheetBehaviour(); initDrawer(); + wikidataEditListener.setAuthenticationStateListener(this); } private void resumeFragment() { @@ -219,7 +228,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp //Still need to check if GPS is enabled checkGps(); lastKnownLocation = locationManager.getLKL(); - refreshView(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED); + refreshView(PERMISSION_JUST_GRANTED); } else { //If permission not granted, go to page that says Nearby Places cannot be displayed hideProgressBar(); @@ -279,7 +288,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp private void checkLocationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (locationManager.isLocationPermissionGranted()) { - refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } else { // Should we show an explanation? if (locationManager.isPermissionExplanationRequired(this)) { @@ -305,7 +314,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp } } } else { - refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } } @@ -314,7 +323,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1) { Timber.d("User is back from Settings page"); - refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } } @@ -373,8 +382,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp @Override public void onReceive(Context context, Intent intent) { if (NetworkUtils.isInternetConnectionEstablished(NearbyActivity.this)) { - refreshView(LocationServiceManager - .LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } else { ViewUtil.showLongToast(NearbyActivity.this, getString(R.string.no_internet)); } @@ -390,7 +398,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp * * @param locationChangeType defines if location shanged significantly or slightly */ - private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) { + private void refreshView(LocationChangeType locationChangeType) { if (lockNearbyView) { return; } @@ -403,12 +411,13 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp registerLocationUpdates(); LatLng lastLocation = locationManager.getLastLocation(); - if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed + if (curLatLng != null && curLatLng.equals(lastLocation) + && !locationChangeType.equals(MAP_UPDATED)) { //refresh view only if location has changed return; } curLatLng = lastLocation; - if (locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) { + if (locationChangeType.equals(PERMISSION_JUST_GRANTED)) { curLatLng = lastKnownLocation; } @@ -417,8 +426,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp return; } - if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) - || locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) { + if (locationChangeType.equals(LOCATION_SIGNIFICANTLY_CHANGED) + || locationChangeType.equals(PERMISSION_JUST_GRANTED) + || locationChangeType.equals(MAP_UPDATED)) { progressBar.setVisibility(View.VISIBLE); //TODO: This hack inserts curLatLng before populatePlaces is called (see #1440). Ideally a proper fix should be found @@ -440,7 +450,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp progressBar.setVisibility(View.GONE); }); } else if (locationChangeType - .equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { + .equals(LOCATION_SLIGHTLY_CHANGED)) { Gson gson = new GsonBuilder() .registerTypeAdapter(Uri.class, new UriSerializer()) .create(); @@ -685,12 +695,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp @Override public void onLocationChangedSignificantly(LatLng latLng) { - refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED); + refreshView(LOCATION_SIGNIFICANTLY_CHANGED); } @Override public void onLocationChangedSlightly(LatLng latLng) { - refreshView(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED); + refreshView(LOCATION_SLIGHTLY_CHANGED); } public void prepareViewsForSheetPosition(int bottomSheetState) { @@ -700,4 +710,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp private void showErrorMessage(String message) { ViewUtil.showLongToast(NearbyActivity.this, message); } + + @Override + public void onWikidataEditSuccessful() { + refreshView(MAP_UPDATED); + } } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java index 1be2a8689..099792bc5 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java @@ -2,6 +2,7 @@ package fr.free.nrw.commons.nearby; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; @@ -21,6 +22,9 @@ import java.lang.reflect.Type; import java.util.Collections; import java.util.List; +import javax.inject.Inject; +import javax.inject.Named; + import dagger.android.support.AndroidSupportInjection; import dagger.android.support.DaggerFragment; import fr.free.nrw.commons.R; @@ -47,6 +51,11 @@ public class NearbyListFragment extends DaggerFragment { private RecyclerView recyclerView; private ContributionController controller; + + @Inject + @Named("direct_nearby_upload_prefs") + SharedPreferences directPrefs; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -137,7 +146,7 @@ public class NearbyListFragment extends DaggerFragment { if (resultCode == RESULT_OK) { Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); - controller.handleImagePicked(requestCode, data, true); + controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null)); } else { Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index 69041d286..934d74353 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -731,6 +731,7 @@ public class NearbyMapFragment extends DaggerFragment { editor.putString("Title", place.getName()); editor.putString("Desc", place.getLongDescription()); editor.putString("Category", place.getCategory()); + editor.putString("WikiDataEntityId", place.getWikiDataEntityId()); editor.apply(); } @@ -766,7 +767,7 @@ public class NearbyMapFragment extends DaggerFragment { if (resultCode == RESULT_OK) { Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); - controller.handleImagePicked(requestCode, data, true); + controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null)); } else { Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s", requestCode, resultCode, data); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java index 9c5138245..93075e8fe 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java @@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby; import android.graphics.Bitmap; import android.net.Uri; import android.support.annotation.DrawableRes; +import android.support.annotation.Nullable; import java.util.HashMap; import java.util.Map; @@ -50,6 +51,20 @@ public class Place { this.distance = distance; } + /** + * Extracts the entity id from the wikidata link + * @return returns the entity id if wikidata link exists + */ + @Nullable + public String getWikiDataEntityId() { + if (!hasWikidataLink()) { + return null; + } + + String wikiDataLink = siteLinks.getWikidataLink().toString(); + return wikiDataLink.replace("http://www.wikidata.org/entity/", ""); + } + public boolean hasWikipediaLink() { return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getWikipediaLink())); } diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java index dc52f198a..b366c944a 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java @@ -16,7 +16,6 @@ import android.widget.RelativeLayout; import com.pedrogomez.renderers.RVRendererAdapter; -import java.lang.ref.WeakReference; import java.util.Collections; import java.util.List; @@ -26,6 +25,7 @@ import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -46,6 +46,8 @@ public class NotificationActivity extends NavigationBaseActivity { @BindView(R.id.container) RelativeLayout relativeLayout; @Inject NotificationController controller; + @Inject + MediaWikiApi mediaWikiApi; private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment"; private NotificationWorkerFragment mNotificationWorkerFragment; @@ -81,7 +83,6 @@ public class NotificationActivity extends NavigationBaseActivity { } } - @SuppressLint("CheckResult") private void addNotifications() { Timber.d("Add notifications"); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 6a59c8e30..40e36e0e0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -8,7 +8,6 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.ContentResolver; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; @@ -69,14 +68,11 @@ import fr.free.nrw.commons.caching.CacheController; import fr.free.nrw.commons.category.CategorizationFragment; import fr.free.nrw.commons.category.OnCategoriesSaveHandler; import fr.free.nrw.commons.contributions.Contribution; -import fr.free.nrw.commons.contributions.ContributionsActivity; import fr.free.nrw.commons.modifications.CategoryModifier; import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.modifications.ModifierSequence; import fr.free.nrw.commons.modifications.ModifierSequenceDao; import fr.free.nrw.commons.modifications.TemplateRemoveModifier; - -import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; @@ -135,6 +131,7 @@ public class ShareActivity private String title; private String description; + private String wikiDataEntityId; private Snackbar snackbar; private boolean duplicateCheckPassed = false; @@ -194,7 +191,7 @@ public class ShareActivity Timber.d("Cache the categories found"); } - uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, c -> { + uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, wikiDataEntityId, c -> { ShareActivity.this.contribution = c; showPostUpload(); }); @@ -278,7 +275,10 @@ public class ShareActivity } if (intent.hasExtra("isDirectUpload")) { Timber.d("This was initiated by a direct upload from Nearby"); - isNearbyUpload = true; + isNearbyUpload = intent.getBooleanExtra("isDirectUpload", false); + } + if (intent.hasExtra("wikiDataEntityId")) { + wikiDataEntityId = intent.getStringExtra("wikiDataEntityId"); } mimeType = intent.getType(); } @@ -446,7 +446,6 @@ public class ShareActivity }); } - @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java index 32554da0f..5fd6d909b 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java @@ -91,7 +91,7 @@ public class UploadController { * @param decimalCoords the coordinates in decimal. (e.g. "37.51136|-77.602615") * @param onComplete the progress tracker */ - public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, ContributionUploadProgress onComplete) { + public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, String wikiDataEntityId, ContributionUploadProgress onComplete) { Contribution contribution; //TODO: Modify this to include coords @@ -101,6 +101,7 @@ public class UploadController { contribution.setTag("mimeType", mimeType); contribution.setSource(source); + contribution.setWikiDataEntityId(wikiDataEntityId); //Calls the next overloaded method startUpload(contribution, onComplete); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java index 94c005256..39d53d2f0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java @@ -18,6 +18,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; +import java.util.Locale; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -36,6 +37,11 @@ import fr.free.nrw.commons.contributions.ContributionsContentProvider; import fr.free.nrw.commons.modifications.ModificationsContentProvider; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.UploadResult; +import fr.free.nrw.commons.utils.ViewUtil; +import fr.free.nrw.commons.wikidata.WikidataEditListener; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; import timber.log.Timber; public class UploadService extends HandlerService { @@ -49,9 +55,11 @@ public class UploadService extends HandlerService { public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign"; @Inject MediaWikiApi mwApi; + @Inject WikidataEditListener wikidataEditListener; @Inject SessionManager sessionManager; @Inject @Named("default_preferences") SharedPreferences prefs; @Inject ContributionDao contributionDao; + @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; private NotificationManager notificationManager; private NotificationCompat.Builder curProgressNotification; @@ -137,6 +145,7 @@ public class UploadService extends HandlerService { @Override public void queue(int what, Contribution contribution) { + Timber.d("Upload service queue has contribution with wiki data entity id as %s", contribution.getWikiDataEntityId()); switch (what) { case ACTION_UPLOAD_FILE: @@ -253,6 +262,7 @@ public class UploadService extends HandlerService { if (!resultStatus.equals("Success")) { showFailedNotification(contribution); } else { + editWikidataProperty(contribution); contribution.setFilename(uploadResult.getCanonicalFilename()); contribution.setImageUrl(uploadResult.getImageUrl()); contribution.setState(Contribution.STATE_COMPLETED); @@ -275,6 +285,48 @@ public class UploadService extends HandlerService { } } + /** + * Edits the wikidata entity by adding the P18 property to it. + * Adding the P18 edit requires calling the wikidata API to create a claim against the entity + * @param contribution + */ + @SuppressLint("CheckResult") + private void editWikidataProperty(Contribution contribution) { + Timber.d("Upload successful with wiki data entity id as %s", contribution.getWikiDataEntityId()); + Timber.d("Attempting to edit Wikidata property %s", contribution.getWikiDataEntityId()); + Observable.fromCallable(() -> mwApi.wikidatCreateClaim(contribution.getWikiDataEntityId(), "P18", "value", getFileName(contribution))) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> handleClaimResult(contribution, result), throwable -> { + Timber.e(throwable, "Error occurred while making claim"); + ViewUtil.showLongToast(getBaseContext(), getResources().getString(R.string.wikidata_edit_failure)); + }); + } + + private void handleClaimResult(Contribution contribution, Boolean result) { + if(result) { + wikidataEditListener.onSuccessfulWikidataEdit(); + String title = directPrefs.getString("Title", ""); + String successStringTemplate = getResources().getString(R.string.successful_wikidata_edit); + String successMessage = String.format(Locale.getDefault(), successStringTemplate, title); + ViewUtil.showLongToast(getBaseContext(), successMessage); + } else { + Timber.d("Unable to make wiki data edit for entity %s", contribution.getWikiDataEntityId()); + ViewUtil.showLongToast(getBaseContext(), getResources().getString(R.string.wikidata_edit_failure)); + } + } + + /** + * Formats and returns the filename as accepted by the wiki base API + * https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim + * @param contribution + * @return + */ + private String getFileName(Contribution contribution) { + String filename = String.format("\"%s\"", contribution.getFilename().replace("File:", "")); + return filename; + } + @SuppressLint("StringFormatInvalid") private void showFailedNotification(Contribution contribution) { Notification failureNotification = new NotificationCompat.Builder(this).setAutoCancel(true) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java new file mode 100644 index 000000000..30fb26ddc --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java @@ -0,0 +1,16 @@ +package fr.free.nrw.commons.wikidata; + +public abstract class WikidataEditListener { + + protected WikidataP18EditListener wikidataP18EditListener; + + public abstract void onSuccessfulWikidataEdit(); + + public void setAuthenticationStateListener(WikidataP18EditListener wikidataP18EditListener) { + this.wikidataP18EditListener = wikidataP18EditListener; + } + + public interface WikidataP18EditListener { + void onWikidataEditSuccessful(); + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java new file mode 100644 index 000000000..407c24711 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.wikidata; + +/** + * Listener for wikidata edits + */ +public class WikidataEditListenerImpl extends WikidataEditListener { + + public WikidataEditListenerImpl() { + } + + /** + * Fired when wikidata P18 edit is successful. If there's an active listener, then it is fired + */ + @Override + public void onSuccessfulWikidataEdit() { + if (wikidataP18EditListener != null) { + wikidataP18EditListener.onWikidataEditSuccessful(); + } + } +} \ 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 6e30baa10..60a977f0b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,4 +284,6 @@ Coordinates were not specified during image selection Error fetching nearby places. + Image successfully added to %1$s on Wikidata! + Failed to update corresponding wiki data entity! 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 686a90ef2..85f1ed98e 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 @@ -26,15 +26,17 @@ class ApacheHttpClientMediaWikiApiTest { private lateinit var testObject: ApacheHttpClientMediaWikiApi private lateinit var server: MockWebServer + private lateinit var wikidataServer: MockWebServer private lateinit var sharedPreferences: SharedPreferences private lateinit var categoryPreferences: SharedPreferences @Before fun setUp() { server = MockWebServer() + wikidataServer = MockWebServer() sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) categoryPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) - testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences, categoryPreferences, Gson()) + testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", "http://" + wikidataServer.hostName + ":" + wikidataServer.port + "/", sharedPreferences, categoryPreferences, Gson()) testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/") } From 30e5312a14f2d9f70a6a342f6b17b34a5500a4b3 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 31 May 2018 08:21:26 +0200 Subject: [PATCH 81/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-de/strings.xml | 2 + app/src/main/res/values-el/strings.xml | 2 + app/src/main/res/values-hu/strings.xml | 4 ++ app/src/main/res/values-iw/strings.xml | 2 + app/src/main/res/values-lb/strings.xml | 2 + app/src/main/res/values-mk/strings.xml | 2 + app/src/main/res/values-ne/strings.xml | 2 + app/src/main/res/values-pt-rBR/strings.xml | 2 + app/src/main/res/values-pt/strings.xml | 2 + app/src/main/res/values-ru/strings.xml | 2 + app/src/main/res/values-sv/strings.xml | 2 + app/src/main/res/values-tr/strings.xml | 4 ++ app/src/main/res/values-uk/strings.xml | 2 + app/src/main/res/values-vi/strings.xml | 67 ++++++++++++++++++++-- app/src/main/res/values-zh-rTW/strings.xml | 2 + 15 files changed, 95 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6fbd7d517..f0eab6cb2 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -278,4 +278,6 @@ App teilen Während der Bildauswahl wurden keine Koordinaten angegeben Fehler beim Abrufen der Orte in der Nähe. + Hintergrundbild festlegen + Hintergrundbild erfolgreich festgelegt! diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 3002482a5..876b76924 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -282,4 +282,6 @@ Κοινοποίηση εφαρμογής Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας Σφάλμα κατά την εύρεση κοντινών μερών. + Ρύθμιση ταπετσαρίας + Η ταπετσαρία ρυθμίστηκε επιτυχώς! diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index fe91781d9..d259e4895 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -59,6 +59,7 @@ Kérlek, adj címet a fájlnak Leírás Nem lehet bejelentkezni - hálózati hiba + Nem sikerült bejelentkezni – kérlek, ellenőrizd a felhasználónevedet és a jelszavadat Túl sok sikertelen próbálkozás. Próbálkozz újra pár perc múlva. Sajnáljuk, ezt a felhasználót blokkolták a Commonson Meg kell adnia a kétlépcsős hitelesítő kódját. @@ -253,15 +254,18 @@ Internet nem elérhető Internet elérhető Nincs értesítés + <u>Fordítás</u> Nyelvek Folytatás Mégse Újra + Értettem! Ezek a helyek vannak a közeledben, amikről van Wikipédia szócikk és nincs bennük kép. A gombra koppintva bejön egy lista, ami ezeket a helyeket mutatja. Bármelyik helyhez feltölthetsz képet a galériádból vagy készíthetsz újat a kamerával. Nem található kép! Képbetöltés közben hiba történt + Feltöltötte: %1$s Alkalmazás megosztása A koordináták nem lettek megadva a kép kiválasztásakor. Hiba a közeli helyek elérésekor. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 03a76a3fc..e13626c29 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -282,4 +282,6 @@ שיתוף היישום לא צוינו קואורדינטות בעת בחירת התמונה שגיאה באחזור המקומות בסביבתך. + הגדרת רקע + הרקע הוגדר בהצלחה! diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml index fa333fa23..e39c06498 100644 --- a/app/src/main/res/values-lb/strings.xml +++ b/app/src/main/res/values-lb/strings.xml @@ -242,4 +242,6 @@ Keng Biller fonnt! Feeler beim Eropluede vu Biller. Eropgeluede vum: %1$s + Hannergrondbild festleeën + Hannergrondbild festgeluecht diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index ee3d94a83..66a48e0a0 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -272,4 +272,6 @@ Сподели прилог Не беа укажани координати при изборот на сликата Грешка при добивањето на околните места. + Задај позадина + Позадината е успешно зададена! diff --git a/app/src/main/res/values-ne/strings.xml b/app/src/main/res/values-ne/strings.xml index 512a655eb..6cd959647 100644 --- a/app/src/main/res/values-ne/strings.xml +++ b/app/src/main/res/values-ne/strings.xml @@ -95,4 +95,6 @@ अज्ञान अनुमतिपत्र ताजागर्ने टगल दृश्य + भित्तेपत्र चयन गर्नुहोस् + भित्तेपत्र सफलतापूर्वक चयन भयो! diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e7b70a35b..f7fa873bf 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -284,4 +284,6 @@ Compartilhar o aplicativo Não foram especificadas coordenadas durante a seleção da imagem Erro ao buscar lugares próximos. + Definir imagem de fundo + Imagem de fundo definida! diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index b995ae220..22776341f 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -283,4 +283,6 @@ Partilhar aplicação Não foram especificadas coordenadas durante a seleção da imagem Erro ao localizar locais próximos. + Definir imagem de fundo + Imagem de fundo definida! diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 747f368d5..754fd228c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -290,4 +290,6 @@ Поделиться приложением Во время выбора изображения не были указаны координаты Ошибка получения мест поблизости + Сделать фоновой заставкой + Фоновая заставка успешно установлена! diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 8c9e8a478..e57c98b5b 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -280,4 +280,6 @@ Dela app Koordinater specificerades inte vid bildvalet Fel uppstod när platser i närheten hämtades. + Ange som bakgrundsbild + Bakgrundsbilden ändrades! diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 784458cfa..c81a11055 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -57,6 +57,7 @@ Lütfen bu dosya için bir başlık ekleyin Açıklama Oturum açılamıyor - ağ hatası + Giriş yapılamıyor - lütfen kullanıcı adınızı ve şifrenizi kontrol edin Çok sayıda başarısız girişimde bulundunuz. Birkaç dakika sonra tekrar deneyin. Üzgünüz, bu kullanıcı Commons\'ta engellendi İki faktörlü kimlik doğrulama kodunu sağlamalısınız. @@ -156,6 +157,7 @@ Telif hakkı olan ve internette bulunan film afişi, kitap kapağı gibi malzemelerin kullanımından kaçının. Bunu anladınız mı? Evet! + * Kategoriler Yükleniyor... Hiçbir şey seçilmedi @@ -279,4 +281,6 @@ Uygulamayı Paylaş Koordinatlar görüntü seçimi sırasında belirlenmedi Yakındaki yerler alınırken hata oluştu. + Duvar kağıdı ayarla + Duvar kağıdı başarıyla ayarlandı! diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index cf6ed0896..4b0286605 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -289,4 +289,6 @@ Поділитися програмою Під час вибору зображення не були вказані координати Помилка отримання місць поблизу. + Поставити шпалерами екрану + Шпалери екрану виставлено успішно! diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index 5b7fd430c..6e32bfdde 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -7,6 +7,8 @@ * Tuankiet65 --> + Giao diện + Tổng quát Phản hồi Vị trí Commons @@ -51,6 +53,7 @@ Xin hãy đặt tiêu đề cho tập tin này Miêu tả Không thể đăng nhập – có lỗi mạng + Không thể đăng nhập – vui lòng kiểm tra tên người dùng và mật khẩu của bạn Đã đăng nhập thất bại quá nhiều lần. Xin vui lòng thử lại trong vòng vài phút. Rất tiếc, người dùng này đã bị cấm tại Commons Bạn phải cung cấp mã xác thực dùng hai nhân tố. @@ -127,11 +130,20 @@ Wikimedia Commons là nơi lưu giữ phần nhiều hình ảnh xuất hiện trong Wikipedia. Các hình ảnh của bạn giúp giáo dục người dân trên khắp thế giới! Xin hãy tải lên các hình ảnh do chính bạn chụp hoặc vẽ: - - Thiên nhiên (bông hoa, thú vật, cảnh núi)\n- Vật nhân tạo (xe đạp, nhà ga, món ăn)\n- Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) + Thiên nhiên (bông hoa, thú vật, cảnh núi)\n* Vật nhân tạo (xe đạp, nhà ga, món ăn)\n* Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) + Thiên nhiên (bông hoa, thú vật, cảnh núi) + Vật nhân tạo (xe đạp, nhà ga, món ăn) + Nhân vật nổi tiếng (thị trưởng của bạn, cầu thủ đội tuyển mà bạn gặp) Xin DỪNG tải lên: - Hình tự sướng hoặc hình bạn bè\n- Hình ảnh tải về từ Internet\n- Ảnh chụp màn hình của ứng dụng thương mại + Ảnh tự chụp hoặc hình bạn bè + Hình ảnh trên Internet tải về + Ảnh chụp màn hình của ứng dụng có bản quyền Tập tin tải lên ví dụ: - - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views + - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Nhà hát Opera Sydney nhìn từ phía tây, Nhà hát Opera Sydney nhìn từ xa + Tiêu đề: Nhà hát Opera Sydney + Miêu tả: Nhà hát Opera Sydney nhìn qua cảng + Thể loại: Nhà hát Opera Sydney nhìn từ phía tây, Nhà hát Opera Sydney nhìn từ xa Đóng góp hình ảnh của bạn. Làm sinh động các bài viết Wikipedia! Các hình ảnh trên Wikipedia được cung cấp bởi Wikimedia Commons. Các hình ảnh của bạn giúp giáo dục người dân trên khắp thế giới. @@ -144,8 +156,8 @@ Không miêu tả Giấy phép không rõ Làm tươi - Yêu cầu cấp phép: Đọc thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép đọc thiết bị lưu trữ bên ngoài để hoạt động. - Yêu cầu cấp phép: Ghi vào thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép ghi vào thiết bị lưu trữ bên ngoài để hoạt động. + Yêu cầu cấp phép: Đọc thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép đọc thiết bị lưu trữ bên ngoài để truy cập kho ảnh của bạn. + Yêu cầu cấp phép: Ghi vào thiết bị lưu trữ bên ngoài. Ứng dụng cần được phép ghi vào thiết bị lưu trữ bên ngoài để truy cập máy chụp hình của bạn. Tùy chọn cấp phép: Định vị hiện tại để nhận gợi ý thể loại OK Nơi Lân cận @@ -158,6 +170,7 @@ Tiêu đề phương tiện Mô tả Mô tả phương tiện xuất hiện tại đây. Nó có thể khá dài và sẽ cần phải ngắt thành nhiều dòng. Nhưng chúng tôi hy vọng sẽ trông ưa nhìn. + Tác giả Ngày tải lên Giấy phép Tọa độ @@ -172,6 +185,9 @@ Hiện chưa hỗ trợ xác thực dùng hai nhân tố. Bạn có chắc chắn muốn đăng xuất? Biểu trưng Commons + Trang Web Commons + Trang Commons tại Facebook + Mã nguồn Commons tại GitHub Hình nền Hình ảnh bị Thất bại Không tìm thấy Hình ảnh @@ -196,15 +212,58 @@ Phản hồi Đăng xuất Hướng dẫn + Thông báo + Chọn lọc Cần có quyền truy cập vị trí của bạn để hiển thị các địa điểm lân cận không tìm thấy miêu tả Trang tập tin Commons Khoản mục Wikidata + Bài Wikipedia Xuất hiện lỗi khi đưa hình ảnh vào vùng nhớ đệm Tên ngắn và duy nhất cho tập tin sẽ được dùng làm tên tập tin. Có thể dùng thuật ngữ bình thường với khoảng cách. Đừng bao gồm phần mở rộng tập tin. Xin vui lòng miêu tả phương tiện càng đầy đủ càng tốt: Chụp ở đâu? Trong hình có gì? Bối cảnh làm sao? Xin vui lòng miêu tả các đối tượng và người trong hình. Cho biết những thông tin khó đoán ra, chẳng hạn giờ trong ngày nếu là phong cảnh. Nếu phương tiện có gì kỳ lạ, xin vui lòng giải thích tại sao nó kỳ lạ. + Cho phép Sử dụng thiết bị lưu trữ bên ngoài Lưu các hình ảnh được chụp bằng máy chụp hình trong ứng dụng vào thiết bị của bạn + Đăng nhập vào tài khoản của bạn + Gửi tập tin nhật trình + Gửi tập tin nhật trình cho nhà phát triển qua thư điện tử + Không tìm thấy trình duyệt để mở URL + Lỗi! Không tìm thấy URL Bầu chọn để xóa + Có đề nghị xóa hình này. Hiện lên ở trang xem browse + Vị trí không thay đổi. + Vị trí không có sẵn. + Bạn cần cho phép để hiển thị danh sách nơi lân cận + HƯỚNG DẪN + ĐỌC BÀI + Hoan nghênh %1$s đã đến Wikimedia Commons! Chúng tôi rất mừng bạn đến đây. + %1$s đã nhắn tin tại trang thảo luận của bạn + Cảm ơn vì bạn đã thực hiện sửa đổi + %1$s đã nhắc đến bạn tại %2$s. + HƯỚNG DẪN + WIKIDATA + WIKIPEDIA + COMMONS + <u>Đánh giá chúng tôi</u> + <u>Câu thường hỏi</u> + Bỏ qua Hướng dẫn + Internet không có sẵn + Internet có sẵn + Lỗi khi lấy thông báo + Không tìm thấy thông báo + <u>Biên dịch</u> + Ngôn ngữ + Chọn ngôn ngữ để gửi bản dịch + Tiến hành + Hủy bỏ + Thử lại + Được rồi! + Không tìm thấy hình ảnh! + Đã xuất hiện lỗi khi tải hình ảnh. + Tải lên bởi: %1$s + Chia sẻ Ứng dụng + Tọa độ không được chỉ định khi chọn hình ảnh + Lỗi khi lấy các nơi lân cận. diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c57406c80..bd1242e12 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -283,4 +283,6 @@ 分享應用程式 當選擇圖片時未指定座標 索取附近地點時出錯。 + 設定桌布 + 桌布設定成功! From edaa0dd05a3e43ecc1a0c5b744338b4c8ae4e531 Mon Sep 17 00:00:00 2001 From: maskara Date: Fri, 1 Jun 2018 01:15:28 +0530 Subject: [PATCH 82/94] Fix build --- .../free/nrw/commons/CommonsApplication.java | 1 + .../commons/di/CommonsApplicationModule.java | 22 +++---------------- .../free/nrw/commons/di/NetworkingModule.java | 2 +- .../nrw/commons/upload/ShareActivity.java | 4 ---- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java index 57cb5fad1..ab6018581 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -21,6 +21,7 @@ import java.io.File; import javax.inject.Inject; import javax.inject.Named; +import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.category.CategoryDao; import fr.free.nrw.commons.contributions.ContributionDao; 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 c5b9f0169..86cce9c03 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 @@ -13,10 +13,13 @@ import javax.inject.Singleton; import dagger.Module; import dagger.Provides; + +import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.location.LocationServiceManager; +import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; import fr.free.nrw.commons.upload.UploadController; @@ -114,31 +117,12 @@ public class CommonsApplicationModule { return new SessionManager(context, mediaWikiApi, sharedPreferences); } - @Provides - @Singleton - public MediaWikiApi provideMediaWikiApi(Context context, - @Named("default_preferences") SharedPreferences defaultPreferences, - @Named("category_prefs") SharedPreferences categoryPrefs, - Gson gson) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson); - } - @Provides @Singleton public LocationServiceManager provideLocationServiceManager(Context context) { return new LocationServiceManager(context); } - /** - * Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere. - * @return returns a singleton Gson instance - */ - @Provides - @Singleton - public Gson provideGson() { - return new Gson(); - } - @Provides @Singleton public DBOpenHelper provideDBOpenHelper(Context context) { 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 8c0b52316..cd043e950 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 @@ -35,7 +35,7 @@ public class NetworkingModule { @Named("default_preferences") SharedPreferences defaultPreferences, @Named("category_prefs") SharedPreferences categoryPrefs, Gson gson) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson); + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson); } @Provides diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index b7a5e0f42..54b9af17a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -139,7 +139,6 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; - private SimpleDraweeView backgroundImageView; private FloatingActionButton maps_fragment; private boolean cacheFound; @@ -163,9 +162,6 @@ public class ShareActivity private Animator CurrentAnimator; private long ShortAnimationDuration; - private FloatingActionButton zoomInButton; - private FloatingActionButton zoomOutButton; - private FloatingActionButton mainFab; private boolean isFABOpen = false; //Had to make them class variables, to extract out the click listeners, also I see no harm in this From c123767ae06d74f1b295ff3b7e2d5561dc6e264c Mon Sep 17 00:00:00 2001 From: Yusuke Matsubara Date: Fri, 1 Jun 2018 15:37:59 +0900 Subject: [PATCH 83/94] Add dependencies to com.android.support.test.rules and runner Needed for ActivityTestRule used in SettingsActivityTest --- app/build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index fad3eccd8..46da62b16 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,6 +75,8 @@ dependencies { androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1' androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION" + androidTestImplementation 'com.android.support.test:rules:1.0.2' + androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY" From 8cd8baf49c972ebbcac00935163db8eb00539362 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 4 Jun 2018 07:53:59 +0200 Subject: [PATCH 84/94] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ast/strings.xml | 3 +++ app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values-ja/error.xml | 5 +++-- app/src/main/res/values-ja/strings.xml | 2 +- app/src/main/res/values-ko/strings.xml | 2 ++ app/src/main/res/values-pl/strings.xml | 2 ++ app/src/main/res/values-pms/strings.xml | 2 ++ app/src/main/res/values-su/strings.xml | 10 +++++----- app/src/main/res/values-zh/strings.xml | 3 +++ 9 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index e92f5dc0a..f7301c45d 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -49,6 +49,7 @@ Apurre un títulu pa esti ficheru Descripción Nun se pudo aniciar sesión – error de rede + Nun pudo aniciase sesión. Revisa\'l nome d\'usuariu y la contraseña Demasiaos intentos incorreutos. Téntalo otra vuelta n\'unos minutos. Sentímoslo, esti usuariu ta bloquiáu en Commons Tienes de dar el códigu d\'identificación de dos factores. @@ -271,4 +272,6 @@ Compartir app Nun s\'especificaron les coordenaes al escoyer la imaxe Error al llograr los llugares cercanos. + Definir fondu + Fondu definíu correutamente diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a93f3011d..e0826cc00 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -289,4 +289,6 @@ Partager les applications Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image Erreur durant l\'exploration du voisinage. + Définir le papier-peint + Papier-peint configuré avec succès! diff --git a/app/src/main/res/values-ja/error.xml b/app/src/main/res/values-ja/error.xml index 9d95a7d2d..bbc722ee1 100644 --- a/app/src/main/res/values-ja/error.xml +++ b/app/src/main/res/values-ja/error.xml @@ -1,10 +1,11 @@ - コモンズがクラッシュしました - エラーが発生しました! + コモンズアプリがクラッシュしました + おおっと、何かおかしいようです! 何をしていたかを記入してメールでお送りください。それをもとに問題点を解決します。 ありがとうございます! diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 78127106f..df73c9d46 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -136,7 +136,7 @@ ウィキメディア・コモンズにはウィキペディアで使用する画像のほぼすべてが保管されています。 あなたの画像は世界中の人々が学習する助けになります! アップロードする画像はあなたご本人が撮影したものかあなたが単独で制作したものに限定します。 - 自然物 (動植物、山)\n• 道具 (自転車、駅)\n• 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) + 自然 (動植物、山)\n• 道具 (自転車、駅)\n• 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) 自然物 (動植物、山) 道具 (自転車、駅) 著名人 (市区村長・都道府県知事、自分が会ったオリンピック選手) diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 8005ce63c..4cd947699 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -275,4 +275,6 @@ 앱 공유 그림 선택 중에 좌표가 지정되지 않았습니다 주변 장소를 가져오는데 오류가 있습니다. + 배경화면 설정 + 배경화면을 성공적으로 설정했습니다! diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index be40b9f8d..d9eff3de6 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -228,4 +228,6 @@ Anuluj Nie znaleziono grafik! Wystąpił błąd podczas ładowania grafik. + Ustaw tapetę + Tapeta ustawiona pomyślnie! diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index fd1fe3d22..c68663306 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -272,4 +272,6 @@ Partagé j\'aplicassion Le coordinà a son nen ëstàite spessificà durant la selession ëd la plancia Eror durant l\'esplorassion dj\'anviron. + Definì la tapissarìa + La tapissarìa a l\'é stàita definìa për da bin! diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml index 3fff0ed27..d209cc1ad 100644 --- a/app/src/main/res/values-su/strings.xml +++ b/app/src/main/res/values-su/strings.xml @@ -12,15 +12,15 @@ Asup log Daptar Asup log - Tungguan heula… - Asup log suksés! - Asup log Gagal! + Tungguan… + Laksana login! + Gagal login! Berkas teu kapanggih. Coba berkas séjén. Oténtikasi gagal! - Ngamimitian ngunjal! + Mitembeyan ngunjal! %1$s diunjal! Toél pikeun némpo unjalan anjeun - Ngamimitian ngunjal %1$s + Ngamimitian %1$s ngunjal Ngunjal %1$s Méréskeun unjalan %1$s Ngunjal %1$s gagal diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index ab638cb87..445e0f82d 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -55,6 +55,7 @@ 请提供此文件的标题 说明 无法登录 - 网络故障 + 无法登录——请检查您的用户名和密码 失败次数过多。请在几分钟后重试。 对不起,该用户已经被共享资源封禁 您必须提供您的双因素验证代码。 @@ -277,4 +278,6 @@ 分享应用 图片选择时,坐标并未指定 检索附近地点时出错。 + 设置墙纸 + 墙纸已成功设置! From f7c57c03ba159b78da5093d5b9b9ec1c10c648ad Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Mon, 4 Jun 2018 13:20:50 +0530 Subject: [PATCH 85/94] Log P18 edits with custom tag (#1583) * Log P18 edits by adding custom tag to the edits * Add javadocs --- .../mwapi/ApacheHttpClientMediaWikiApi.java | 39 +++++- .../free/nrw/commons/mwapi/MediaWikiApi.java | 5 +- .../nrw/commons/upload/UploadService.java | 49 +------ .../commons/wikidata/WikidataEditService.java | 130 ++++++++++++++++++ 4 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index 3eeeec4e2..96bf9cbcf 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -394,12 +394,13 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { * @param property the property to be edited, for eg P18 for images * @param snaktype the type of value stored for that property * @param value the actual value to be stored for the property, for eg filename in case of P18 - * @return returns true if the claim is successfully created + * @return returns revisionId if the claim is successfully created else returns null * @throws IOException */ @Nullable @Override - public boolean wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException { + public String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException { + Timber.d("Filename is %s", value); ApiResult result = wikidataApi.action("wbcreateclaim") .param("entity", entityId) .param("centralauthtoken", getCentralAuthToken()) @@ -410,13 +411,45 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .post(); if (result == null || result.getNode("api") == null) { - return false; + return null; } Node node = result.getNode("api").getDocument(); Element element = (Element) node; if (element != null && element.getAttribute("success").equals("1")) { + return result.getString("api/pageinfo/@lastrevid"); + } else { + Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info")); + } + return null; + } + + /** + * Adds the wikimedia-commons-app tag to the edits made on wikidata + * @param revisionId + * @return + * @throws IOException + */ + @Nullable + @Override + public boolean addWikidataEditTag(String revisionId) throws IOException { + ApiResult result = wikidataApi.action("tag") + .param("revid", revisionId) + .param("centralauthtoken", getCentralAuthToken()) + .param("token", getWikidataCsrfToken()) + .param("add", "wikimedia-commons-app") + .param("reason", "Add tag for edits made using Android Commons app") + .post(); + + if (result == null || result.getNode("api") == null) { + return false; + } + + Node node = result.getNode("api").getDocument(); + Element element = (Element) node; + + if (element != null && element.getAttribute("status").equals("success")) { return true; } else { Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info")); diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java index 869205f79..b4398319f 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java @@ -54,7 +54,10 @@ public interface MediaWikiApi { String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException; @Nullable - boolean wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException; + String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException; + + @Nullable + boolean addWikidataEditTag(String revisionId) throws IOException; @NonNull MediaResult fetchMediaByFilename(String filename) throws IOException; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java index 39d53d2f0..4c05eb7b0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java @@ -39,6 +39,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.mwapi.UploadResult; import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.wikidata.WikidataEditListener; +import fr.free.nrw.commons.wikidata.WikidataEditService; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -55,11 +56,9 @@ public class UploadService extends HandlerService { public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign"; @Inject MediaWikiApi mwApi; - @Inject WikidataEditListener wikidataEditListener; + @Inject WikidataEditService wikidataEditService; @Inject SessionManager sessionManager; - @Inject @Named("default_preferences") SharedPreferences prefs; @Inject ContributionDao contributionDao; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; private NotificationManager notificationManager; private NotificationCompat.Builder curProgressNotification; @@ -262,7 +261,7 @@ public class UploadService extends HandlerService { if (!resultStatus.equals("Success")) { showFailedNotification(contribution); } else { - editWikidataProperty(contribution); + wikidataEditService.createClaimWithLogging(contribution.getWikiDataEntityId(), filename); contribution.setFilename(uploadResult.getCanonicalFilename()); contribution.setImageUrl(uploadResult.getImageUrl()); contribution.setState(Contribution.STATE_COMPLETED); @@ -285,48 +284,6 @@ public class UploadService extends HandlerService { } } - /** - * Edits the wikidata entity by adding the P18 property to it. - * Adding the P18 edit requires calling the wikidata API to create a claim against the entity - * @param contribution - */ - @SuppressLint("CheckResult") - private void editWikidataProperty(Contribution contribution) { - Timber.d("Upload successful with wiki data entity id as %s", contribution.getWikiDataEntityId()); - Timber.d("Attempting to edit Wikidata property %s", contribution.getWikiDataEntityId()); - Observable.fromCallable(() -> mwApi.wikidatCreateClaim(contribution.getWikiDataEntityId(), "P18", "value", getFileName(contribution))) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> handleClaimResult(contribution, result), throwable -> { - Timber.e(throwable, "Error occurred while making claim"); - ViewUtil.showLongToast(getBaseContext(), getResources().getString(R.string.wikidata_edit_failure)); - }); - } - - private void handleClaimResult(Contribution contribution, Boolean result) { - if(result) { - wikidataEditListener.onSuccessfulWikidataEdit(); - String title = directPrefs.getString("Title", ""); - String successStringTemplate = getResources().getString(R.string.successful_wikidata_edit); - String successMessage = String.format(Locale.getDefault(), successStringTemplate, title); - ViewUtil.showLongToast(getBaseContext(), successMessage); - } else { - Timber.d("Unable to make wiki data edit for entity %s", contribution.getWikiDataEntityId()); - ViewUtil.showLongToast(getBaseContext(), getResources().getString(R.string.wikidata_edit_failure)); - } - } - - /** - * Formats and returns the filename as accepted by the wiki base API - * https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim - * @param contribution - * @return - */ - private String getFileName(Contribution contribution) { - String filename = String.format("\"%s\"", contribution.getFilename().replace("File:", "")); - return filename; - } - @SuppressLint("StringFormatInvalid") private void showFailedNotification(Contribution contribution) { Notification failureNotification = new NotificationCompat.Builder(this).setAutoCancel(true) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java new file mode 100644 index 000000000..b43ed329c --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -0,0 +1,130 @@ +package fr.free.nrw.commons.wikidata; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.Locale; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import fr.free.nrw.commons.R; +import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.utils.ViewUtil; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +/** + * This class is meant to handle the Wikidata edits made through the app + * It will talk with MediaWikiApi to make necessary API calls, log the edits and fire listeners + * on successful edits + */ +@Singleton +public class WikidataEditService { + + private final Context context; + private final MediaWikiApi mediaWikiApi; + private final WikidataEditListener wikidataEditListener; + private final SharedPreferences directPrefs; + + @Inject + public WikidataEditService(Context context, + MediaWikiApi mediaWikiApi, + WikidataEditListener wikidataEditListener, + @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) { + this.context = context; + this.mediaWikiApi = mediaWikiApi; + this.wikidataEditListener = wikidataEditListener; + this.directPrefs = directPrefs; + } + + /** + * Create a P18 claim and log the edit with custom tag + * @param wikidataEntityId + * @param fileName + */ + public void createClaimWithLogging(String wikidataEntityId, String fileName) { + editWikidataProperty(wikidataEntityId, fileName); + } + + /** + * Edits the wikidata entity by adding the P18 property to it. + * Adding the P18 edit requires calling the wikidata API to create a claim against the entity + * + * @param wikidataEntityId + * @param fileName + */ + @SuppressLint("CheckResult") + private void editWikidataProperty(String wikidataEntityId, String fileName) { + Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId); + Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId); + Observable.fromCallable(() -> { + String propertyValue = getFileName(fileName); + return mediaWikiApi.wikidatCreateClaim(wikidataEntityId, "P18", "value", propertyValue); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(revisionId -> handleClaimResult(wikidataEntityId, revisionId), throwable -> { + Timber.e(throwable, "Error occurred while making claim"); + ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure)); + }); + } + + private void handleClaimResult(String wikidataEntityId, String revisionId) { + if (revisionId != null) { + wikidataEditListener.onSuccessfulWikidataEdit(); + showSuccessToast(); + logEdit(revisionId); + } else { + Timber.d("Unable to make wiki data edit for entity %s", wikidataEntityId); + ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure)); + } + } + + /** + * Log the Wikidata edit by adding Wikimedia Commons App tag to the edit + * @param revisionId + */ + @SuppressLint("CheckResult") + private void logEdit(String revisionId) { + Observable.fromCallable(() -> mediaWikiApi.addWikidataEditTag(revisionId)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result) { + Timber.d("Wikidata edit was tagged successfully"); + } else { + Timber.d("Wikidata edit couldn't be tagged"); + } + }, throwable -> { + Timber.e(throwable, "Error occurred while adding tag to the edit"); + }); + } + + /** + * Show a success toast when the edit is made successfully + */ + private void showSuccessToast() { + String title = directPrefs.getString("Title", ""); + String successStringTemplate = context.getString(R.string.successful_wikidata_edit); + String successMessage = String.format(Locale.getDefault(), successStringTemplate, title); + ViewUtil.showLongToast(context, successMessage); + } + + /** + * Formats and returns the filename as accepted by the wiki base API + * https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim + * + * @param fileName + * @return + */ + private String getFileName(String fileName) { + fileName = String.format("\"%s\"", fileName.replace("File:", "")); + Timber.d("Wikidata property name is %s", fileName); + return fileName; + } +} From 147367d75f40b93690c030a4f0fa42ae31699e1e Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:25:29 +1000 Subject: [PATCH 86/94] Get decimal coords to pass into startUpload() --- .../free/nrw/commons/upload/FileProcessor.java | 8 +++++--- .../free/nrw/commons/upload/ShareActivity.java | 18 +++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) 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 88e1c426d..5cddaaade 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 @@ -95,13 +95,11 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ return filePath; } - //TODO: Figure out why coords are not sent to location template, use LocationServiceManager - /** * Gets coordinates for category suggestions, either from EXIF data or user location * @param gpsEnabled if true use GPS */ - GPSExtractor getFileCoordinates(boolean gpsEnabled) { + GPSExtractor processFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); @@ -132,6 +130,10 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ return imageObj; } + String getDecimalCoords() { + return decimalCoords; + } + /** * Find other images around the same location that were taken within the last 20 sec * @param gpsEnabled True if GPS is enabled diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index ffb33f545..7a59e336a 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -124,7 +124,7 @@ public class ShareActivity private Uri mediaUri; private Contribution contribution; private boolean cacheFound; - private GPSExtractor imageObj; + private GPSExtractor gpsObj; private GPSExtractor tempImageObj; private String decimalCoords; private FileProcessor fileObj; @@ -180,7 +180,7 @@ public class ShareActivity * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController */ private void uploadBegins() { - fileObj.getFileCoordinates(locationPermitted); + fileObj.processFileCoordinates(locationPermitted); Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG); startingToast.show(); @@ -191,6 +191,9 @@ public class ShareActivity Timber.d("Cache the categories found"); } + + //TODO: Figure out why coords are not sent to location template, use LocationServiceManager + uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, c -> { ShareActivity.this.contribution = c; showPostUpload(); @@ -306,7 +309,8 @@ public class ShareActivity ContentResolver contentResolver = this.getContentResolver(); fileObj = new FileProcessor(mediaUri, contentResolver, this); checkIfFileExists(); - imageObj = fileObj.getFileCoordinates(locationPermitted); + gpsObj = fileObj.processFileCoordinates(locationPermitted); + decimalCoords = fileObj.getDecimalCoords(); } /** @@ -340,7 +344,7 @@ public class ShareActivity private void showFABMenu() { isFABOpen=true; - if( imageObj != null && imageObj.imageCoordsExists) + if( gpsObj != null && gpsObj.imageCoordsExists) mapButton.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); @@ -468,7 +472,7 @@ public class ShareActivity public void onPause() { super.onPause(); try { - imageObj.unregisterLocationManager(); + gpsObj.unregisterLocationManager(); Timber.d("Unregistered locationManager"); } catch (NullPointerException e) { Timber.d("locationManager does not exist, not unregistered"); @@ -626,8 +630,8 @@ public class ShareActivity @OnClick(R.id.media_map) public void onFabShowMapsClicked() { - if (imageObj != null && imageObj.imageCoordsExists) { - Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj.getDecLongitude()); + if (gpsObj != null && gpsObj.imageCoordsExists) { + Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + gpsObj.getDecLatitude() + "," + gpsObj.getDecLongitude()); Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); mapIntent.setPackage("com.google.android.apps.maps"); startActivity(mapIntent); From 4e1bf8036135ef817eaf17d9b6bf412356c5c80f Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:30:18 +1000 Subject: [PATCH 87/94] Fix comments --- .../main/java/fr/free/nrw/commons/upload/FileProcessor.java | 2 +- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) 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 5cddaaade..3fc3e55b7 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 @@ -96,7 +96,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ } /** - * Gets coordinates for category suggestions, either from EXIF data or user location + * Processes file coordinates, either from EXIF data or user location * @param gpsEnabled if true use GPS */ GPSExtractor processFileCoordinates(boolean gpsEnabled) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 7a59e336a..02cb1ac27 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -191,9 +191,6 @@ public class ShareActivity Timber.d("Cache the categories found"); } - - //TODO: Figure out why coords are not sent to location template, use LocationServiceManager - uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, c -> { ShareActivity.this.contribution = c; showPostUpload(); From c9ae7176c0743844e1a6c2d8bee3979b90a8afbc Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:40:49 +1000 Subject: [PATCH 88/94] Optimize formatting and imports --- .../nrw/commons/upload/FileProcessor.java | 91 ++++++++++--------- .../nrw/commons/upload/ShareActivity.java | 22 +++-- 2 files changed, 59 insertions(+), 54 deletions(-) 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 3fc3e55b7..2845b8d1f 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 @@ -33,18 +33,7 @@ import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; /** * Processing of the image file that is about to be uploaded via ShareActivity is done here */ -public class FileProcessor implements SimilarImageDialogFragment.onResponse{ - - private Uri mediaUri; - private ContentResolver contentResolver; - private GPSExtractor imageObj; - private Context context; - private String decimalCoords; - private boolean haveCheckedForOtherImages = false; - private String filePath; - private boolean useExtStorage; - private boolean cacheFound; - private GPSExtractor tempImageObj; +public class FileProcessor implements SimilarImageDialogFragment.onResponse { @Inject CacheController cacheController; @@ -55,6 +44,16 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ @Inject @Named("default_preferences") SharedPreferences prefs; + private Uri mediaUri; + private ContentResolver contentResolver; + private GPSExtractor imageObj; + private Context context; + private String decimalCoords; + private boolean haveCheckedForOtherImages = false; + private String filePath; + private boolean useExtStorage; + private boolean cacheFound; + private GPSExtractor tempImageObj; FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) { this.mediaUri = mediaUri; @@ -67,6 +66,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ /** * Gets file path from media URI. * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead. + * * @return file path of media */ @Nullable @@ -97,32 +97,32 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ /** * Processes file coordinates, either from EXIF data or user location + * * @param gpsEnabled if true use GPS */ GPSExtractor processFileCoordinates(boolean gpsEnabled) { Timber.d("Calling GPSExtractor"); try { - ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - if (descriptor != null) { - imageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs); - } - } else { - String filePath = getPathOfMediaOrCopy(); - if (filePath != null) { - imageObj = new GPSExtractor(filePath, context, prefs); - } + ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + if (descriptor != null) { + imageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs); + } + } else { + String filePath = getPathOfMediaOrCopy(); + if (filePath != null) { + imageObj = new GPSExtractor(filePath, context, prefs); + } } - decimalCoords = imageObj.getCoords(gpsEnabled); - if (decimalCoords == null || !imageObj.imageCoordsExists){ - //Find other photos taken around the same time which has gps coordinates - if(!haveCheckedForOtherImages) - findOtherImages(gpsEnabled);// Do not do repeat the process - } - else { - useImageCoords(); - } + decimalCoords = imageObj.getCoords(gpsEnabled); + if (decimalCoords == null || !imageObj.imageCoordsExists) { + //Find other photos taken around the same time which has gps coordinates + if (!haveCheckedForOtherImages) + findOtherImages(gpsEnabled);// Do not do repeat the process + } else { + useImageCoords(); + } } catch (FileNotFoundException e) { Timber.w("File not found: " + mediaUri, e); @@ -136,21 +136,22 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ /** * Find other images around the same location that were taken within the last 20 sec + * * @param gpsEnabled True if GPS is enabled */ private void findOtherImages(boolean gpsEnabled) { - Timber.d("filePath"+getPathOfMediaOrCopy()); + Timber.d("filePath" + getPathOfMediaOrCopy()); long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created - File folder = new File(filePath.substring(0,filePath.lastIndexOf('/'))); + File folder = new File(filePath.substring(0, filePath.lastIndexOf('/'))); File[] files = folder.listFiles(); - Timber.d("folderTime Number:"+files.length); + Timber.d("folderTime Number:" + files.length); - for(File file : files){ - if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){ + for (File file : files) { + if (file.lastModified() - timeOfCreation <= (120 * 1000) && file.lastModified() - timeOfCreation >= -(120 * 1000)) { //Make sure the photos were taken within 20seconds - Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation); + Timber.d("fild date:" + file.lastModified() + " time of creation" + timeOfCreation); tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos ParcelFileDescriptor descriptor = null; try { @@ -168,17 +169,17 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ } } - if(tempImageObj!=null){ - Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled)); - if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){ + if (tempImageObj != null) { + Timber.d("not null fild EXIF" + tempImageObj.imageCoordsExists + " coords" + tempImageObj.getCoords(gpsEnabled)); + if (tempImageObj.getCoords(gpsEnabled) != null && tempImageObj.imageCoordsExists) { // Current image has gps coordinates and it's not current gps locaiton - Timber.d("This file has image coords:"+ file.getAbsolutePath()); + Timber.d("This file has image coords:" + file.getAbsolutePath()); SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment(); Bundle args = new Bundle(); - args.putString("originalImagePath",filePath); - args.putString("possibleImagePath",file.getAbsolutePath()); + args.putString("originalImagePath", filePath); + args.putString("possibleImagePath", file.getAbsolutePath()); newFragment.setArguments(args); - newFragment.show(((AppCompatActivity)context).getSupportFragmentManager(), "dialog"); + newFragment.show(((AppCompatActivity) context).getSupportFragmentManager(), "dialog"); break; } } @@ -195,7 +196,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse{ public void useImageCoords() { if (decimalCoords != null) { Timber.d("Decimal coords of image: %s", decimalCoords); - Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image"); + Timber.d("is EXIF data present:" + imageObj.imageCoordsExists + " from findOther image"); // Only set cache for this point if image has coords if (imageObj.imageCoordsExists) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 02cb1ac27..3ae9bae96 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -166,6 +166,7 @@ public class ShareActivity /** * Checks whether storage permissions need to be requested. * Permissions are needed if the file is not owned by this application, (e.g. shared from the Gallery) + * * @return true if file is not owned by this application and permission hasn't been granted beforehand */ @RequiresApi(16) @@ -211,6 +212,7 @@ public class ShareActivity /** * Send categories to modifications queue after they are selected + * * @param categories categories selected */ @Override @@ -339,9 +341,9 @@ public class ShareActivity * Function to display the zoom and map FAB */ private void showFABMenu() { - isFABOpen=true; + isFABOpen = true; - if( gpsObj != null && gpsObj.imageCoordsExists) + if (gpsObj != null && gpsObj.imageCoordsExists) mapButton.setVisibility(View.VISIBLE); zoomInButton.setVisibility(View.VISIBLE); @@ -353,8 +355,8 @@ public class ShareActivity /** * Function to close the zoom and map FAB */ - private void closeFABMenu(){ - isFABOpen=false; + private void closeFABMenu() { + isFABOpen = false; mainFab.animate().rotationBy(-180); mapButton.animate().translationY(0); zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() { @@ -364,7 +366,7 @@ public class ShareActivity @Override public void onAnimationEnd(Animator animator) { - if(!isFABOpen){ + if (!isFABOpen) { mapButton.setVisibility(View.GONE); zoomInButton.setVisibility(View.GONE); } @@ -382,6 +384,7 @@ public class ShareActivity /** * Checks if upload was initiated via Nearby + * * @return true if upload was initiated via Nearby */ protected boolean isNearbyUpload() { @@ -390,8 +393,9 @@ public class ShareActivity /** * Handles BOTH snackbar permission request (for location) and submit button permission request (for storage) - * @param requestCode type of request - * @param permissions permissions requested + * + * @param requestCode type of request + * @param permissions permissions requested * @param grantResults grant results */ @Override @@ -422,7 +426,7 @@ public class ShareActivity } /** - * Displays Snackbar to ask for location permissions + * Displays Snackbar to ask for location permissions */ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) { Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), rationale, @@ -453,7 +457,7 @@ public class ShareActivity //image is not a duplicate, so now check if its a unwanted picture or not fileObj.detectUnwantedPictures(); } - },mwApi); + }, mwApi); fileAsyncTask.execute(); } catch (IOException e) { Timber.e(e, "IO Exception: "); From b4f240404597bc5d07b01b8ab2f730f7fdbf971f Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:41:44 +1000 Subject: [PATCH 89/94] Remove unnecessary vars --- .../main/java/fr/free/nrw/commons/upload/ShareActivity.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java index 3ae9bae96..3f67696b0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java @@ -76,10 +76,7 @@ public class ShareActivity extends AuthenticatedActivity implements SingleUploadFragment.OnUploadActionInitiated, OnCategoriesSaveHandler { - - private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1; private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2; - private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3; private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4; //Had to make them class variables, to extract out the click listeners, also I see no harm in this final Rect startBounds = new Rect(); @@ -123,9 +120,7 @@ public class ShareActivity private CategorizationFragment categorizationFragment; private Uri mediaUri; private Contribution contribution; - private boolean cacheFound; private GPSExtractor gpsObj; - private GPSExtractor tempImageObj; private String decimalCoords; private FileProcessor fileObj; private boolean useNewPermissions = false; From 90334edd32d50d6762f146fdb9fcd526d5721201 Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:45:10 +1000 Subject: [PATCH 90/94] Remove unnecessary class --- .../main/java/fr/free/nrw/commons/upload/UploadUtils.java | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java deleted file mode 100644 index 0c0703ca6..000000000 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadUtils.java +++ /dev/null @@ -1,7 +0,0 @@ -package fr.free.nrw.commons.upload; - -public class UploadUtils { - - - -} From 3ccfcac130dabd1999e339d8795b4663b1f33a08 Mon Sep 17 00:00:00 2001 From: misaochan Date: Mon, 4 Jun 2018 19:52:14 +1000 Subject: [PATCH 91/94] Reinstate MultipleShareActivity intent --- app/src/main/AndroidManifest.xml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 88111063a..80bc59c27 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -27,10 +27,10 @@ android:theme="@style/LightAppTheme" android:supportsRtl="true" > + android:theme="@android:style/Theme.Dialog" + android:launchMode="singleInstance" + android:excludeFromRecents="true" + android:finishOnTaskLaunch="true" /> @@ -47,6 +47,17 @@ android:label="@string/app_name"> + + + + + + + + From d1b33933cf3e1143566a10c03dd1844459ad5faa Mon Sep 17 00:00:00 2001 From: maskara Date: Tue, 5 Jun 2018 00:03:56 +0530 Subject: [PATCH 92/94] Fix build failure --- .../test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 292073bbf..84f6d5f10 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -72,8 +72,6 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager - override fun provideMediaWikiApi(context: Context, sharedPreferences: SharedPreferences, categorySharedPreferences: SharedPreferences, gson: Gson): MediaWikiApi = mediaWikiApi - override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager override fun provideDBOpenHelper(context: Context): DBOpenHelper = mockDbOpenHelper From ed5cc82fa224ec0ade20b73031eb803e932776ca Mon Sep 17 00:00:00 2001 From: maskara Date: Tue, 5 Jun 2018 00:28:49 +0530 Subject: [PATCH 93/94] Check for null entity ID --- .../fr/free/nrw/commons/wikidata/WikidataEditService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java index b43ed329c..8bff40b89 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -48,6 +48,10 @@ public class WikidataEditService { * @param fileName */ public void createClaimWithLogging(String wikidataEntityId, String fileName) { + if(wikidataEntityId == null + || fileName == null) { + return; + } editWikidataProperty(wikidataEntityId, fileName); } From 862318c578127de5ffc34b7ca950c3a5e263f601 Mon Sep 17 00:00:00 2001 From: Yusuke Matsubara Date: Tue, 5 Jun 2018 18:33:51 +0900 Subject: [PATCH 94/94] Bump max line length to 140 characters in Checkstyle rules (#1590) Fixes #465 --- script/style/checkstyle.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/script/style/checkstyle.xml b/script/style/checkstyle.xml index cb0b13dca..216d1bce2 100644 --- a/script/style/checkstyle.xml +++ b/script/style/checkstyle.xml @@ -7,6 +7,8 @@ Modified from https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml. Modifications are: - doubled each value in Indentation + - exceptions for Butter Knife and Espresso + - larger (max)LineLength Checkstyle configuration that checks the Google coding conventions from Google Java Style that can be found at https://google.github.io/styleguide/javaguide.html. @@ -44,7 +46,7 @@ - + @@ -56,7 +58,7 @@ - +