diff --git a/app/src/androidTest/java/fr/free/nrw/commons/MediaTest.java b/app/src/androidTest/java/fr/free/nrw/commons/MediaTest.java
new file mode 100644
index 000000000..ecb465293
--- /dev/null
+++ b/app/src/androidTest/java/fr/free/nrw/commons/MediaTest.java
@@ -0,0 +1,23 @@
+package fr.free.nrw.commons;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.hamcrest.CoreMatchers.is;
+
+// TODO: use Robolectric and make it runnable without a connected device
+@RunWith(AndroidJUnit4.class)
+public class MediaTest {
+ @Test public void displayTitleShouldStripExtension() {
+ Media m = new Media("File:Example.jpg");
+ Assert.assertThat(m.getDisplayTitle(), is("Example"));
+ }
+
+ @Test public void displayTitleShouldUseSpaceForUnderscore() {
+ Media m = new Media("File:Example 1_2.jpg");
+ Assert.assertThat(m.getDisplayTitle(), is("Example 1 2"));
+ }
+}
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/PageTitleTest.java b/app/src/androidTest/java/fr/free/nrw/commons/PageTitleTest.java
new file mode 100644
index 000000000..5c83ef691
--- /dev/null
+++ b/app/src/androidTest/java/fr/free/nrw/commons/PageTitleTest.java
@@ -0,0 +1,61 @@
+package fr.free.nrw.commons;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.URLEncoder;
+
+import static org.hamcrest.CoreMatchers.is;
+
+// TODO: use Robolectric and make it runnable without a connected device
+@RunWith(AndroidJUnit4.class)
+public class PageTitleTest {
+ @Test public void displayTextShouldNotBeUnderscored() {
+ Assert.assertThat(new PageTitle("Ex_1 ").getDisplayText(),
+ is("Ex 1"));
+ }
+
+ @Test public void moreThanTwoColons() {
+ Assert.assertThat(new PageTitle("File:sample:a.jpg").getPrefixedText(),
+ is("File:Sample:a.jpg"));
+ }
+
+ @Test public void getTextShouldReturnWithoutNamespace() {
+ Assert.assertThat(new PageTitle("File:sample.jpg").getText(),
+ is("Sample.jpg"));
+ }
+
+
+ @Test public void capitalizeNameAfterNamespace() {
+ Assert.assertThat(new PageTitle("File:sample.jpg").getPrefixedText(),
+ is("File:Sample.jpg"));
+ }
+
+ @Test public void prefixedTextShouldBeUnderscored() {
+ Assert.assertThat(new PageTitle("Ex 1 ").getPrefixedText(),
+ is("Ex_1"));
+ }
+
+ @Test public void getMobileUriForTest() {
+ Assert.assertThat(new PageTitle("Test").getMobileUri().toString(),
+ is("https://commons.m.wikimedia.org/wiki/Test"));
+ }
+
+ @Test public void spaceBecomesUnderscoreInUri() {
+ Assert.assertThat(new PageTitle("File:Ex 1.jpg").getCanonicalUri().toString(),
+ is("https://commons.wikimedia.org/wiki/File:Ex_1.jpg"));
+ }
+
+ @Test public void leaveSubpageNamesUncapitalizedInUri() {
+ Assert.assertThat(new PageTitle("User:Ex/subpage").getCanonicalUri().toString(),
+ is("https://commons.wikimedia.org/wiki/User:Ex/subpage"));
+ }
+
+ @Test public void unicodeUri() throws Throwable {
+ Assert.assertThat(new PageTitle("User:例").getCanonicalUri().toString(),
+ is("https://commons.wikimedia.org/wiki/User:" + URLEncoder.encode("例", "utf-8")));
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/LicenseList.java b/app/src/main/java/fr/free/nrw/commons/LicenseList.java
index f3edf3be0..28d5b3f34 100644
--- a/app/src/main/java/fr/free/nrw/commons/LicenseList.java
+++ b/app/src/main/java/fr/free/nrw/commons/LicenseList.java
@@ -2,6 +2,7 @@ package fr.free.nrw.commons;
import android.app.Activity;
import android.content.res.Resources;
+import android.support.annotation.Nullable;
import org.xmlpull.v1.XmlPullParser;
@@ -42,10 +43,11 @@ public class LicenseList {
return licenses.get(key);
}
+ @Nullable
public License licenseForTemplate(String template) {
- String ucTemplate = Utils.capitalize(template);
+ String ucTemplate = new PageTitle(template).getDisplayText();
for (License license : values()) {
- if (ucTemplate.equals(Utils.capitalize(license.getTemplate()))) {
+ if (ucTemplate.equals(new PageTitle(license.getTemplate()).getDisplayText())) {
return license;
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/Media.java b/app/src/main/java/fr/free/nrw/commons/Media.java
index e3fb2baaf..d8f73cae9 100644
--- a/app/src/main/java/fr/free/nrw/commons/Media.java
+++ b/app/src/main/java/fr/free/nrw/commons/Media.java
@@ -47,7 +47,7 @@ public class Media implements Parcelable {
return "";
}
// FIXME: Gross hack bercause my regex skills suck maybe or I am too lazy who knows
- String title = filename.replaceFirst("^File:", "");
+ String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", "");
Matcher matcher = displayTitlePattern.matcher(title);
if(matcher.matches()) {
return matcher.group(1);
@@ -56,13 +56,8 @@ public class Media implements Parcelable {
}
}
- public String getDescriptionUrl() {
- // HACK! Geez
- return CommonsApplication.HOME_URL + "File:" + Utils.urlEncode(getFilename().replace("File:", "").replace(" ", "_"));
- }
-
- public String getMobileDescriptionUrl() {
- return CommonsApplication.MOBILE_HOME_URL + "File:" + Utils.urlEncode(getFilename().replace("File:", "").replace(" ", "_"));
+ public PageTitle getFilePageTitle() {
+ return new PageTitle("File:" + getFilename().replaceFirst("^File:", ""));
}
public Uri getLocalUri() {
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 698adb683..978c06115 100644
--- a/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java
+++ b/app/src/main/java/fr/free/nrw/commons/MediaDataExtractor.java
@@ -150,14 +150,14 @@ public class MediaDataExtractor {
}
}
- private Node findTemplate(Element parentNode, String title) throws IOException {
- String ucTitle= Utils.capitalize(title);
+ private Node findTemplate(Element parentNode, String title_) throws IOException {
+ String title= new PageTitle(title_).getDisplayText();
NodeList nodes = parentNode.getChildNodes();
for (int i = 0, length = nodes.getLength(); i < length; i++) {
Node node = nodes.item(i);
if (node.getNodeName().equals("template")) {
String foundTitle = getTemplateTitle(node);
- if (Utils.capitalize(foundTitle).equals(ucTitle)) {
+ if (title.equals(new PageTitle(foundTitle).getDisplayText())) {
return node;
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java b/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java
index bd64f77cb..3e147f4a8 100644
--- a/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java
+++ b/app/src/main/java/fr/free/nrw/commons/MediaWikiImageView.java
@@ -6,10 +6,13 @@ import android.support.annotation.Nullable;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.widget.Toast;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.SimpleDraweeView;
+import timber.log.Timber;
+
public class MediaWikiImageView extends SimpleDraweeView {
private ThumbnailFetchTask currentThumbnailTask;
@@ -81,7 +84,13 @@ public class MediaWikiImageView extends SimpleDraweeView {
result = media.getLocalUri().toString();
} else {
// only cache meaningful thumbnails received from network.
- CommonsApplication.getInstance().getThumbnailUrlCache().put(media.getFilename(), result);
+ try {
+ CommonsApplication.getInstance().getThumbnailUrlCache().put(media.getFilename(), result);
+ } catch (NullPointerException npe) {
+ Timber.e("error when adding pic to cache " + npe);
+
+ Toast.makeText(getContext(), R.string.error_while_cache, Toast.LENGTH_SHORT).show();
+ }
}
setImageUrl(result);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/PageTitle.java b/app/src/main/java/fr/free/nrw/commons/PageTitle.java
new file mode 100644
index 000000000..eb8a61284
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/PageTitle.java
@@ -0,0 +1,91 @@
+package fr.free.nrw.commons;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+public class PageTitle {
+ private final String namespace;
+ private final String titleKey;
+
+ /**
+ * Construct from a namespace-prefixed page name.
+ * @param prefixedText namespace-prefixed page name
+ */
+ public PageTitle(@NonNull String prefixedText) {
+ String[] segments = prefixedText.trim().replace(" ", "_").split(":", 2);
+
+ // canonicalize and capitalize page title as done by MediaWiki
+ if (segments.length == 2) {
+ // TODO: canonicalize and capitalize namespace as well
+ // see https://www.mediawiki.org/wiki/Manual:Title.php#Canonical_forms
+ namespace = segments[0];
+ titleKey = Utils.capitalize(segments[1]);
+ } else {
+ namespace = "";
+ titleKey = Utils.capitalize(segments[0]);
+ }
+ }
+
+ /**
+ * Get the canonicalized title for displaying (such as "File:My example.jpg").
+ *
+ * @return canonical title
+ */
+ @NonNull
+ public String getPrefixedText() {
+ if (namespace.isEmpty()) {
+ return titleKey;
+ } else {
+ return namespace + ":" + titleKey;
+ }
+ }
+
+ /**
+ * Get the canonical title for DB and URLs (such as "File:My_example.jpg").
+ *
+ * @return canonical title
+ */
+ @NonNull
+ public String getDisplayText() {
+ return getPrefixedText().replace("_", " ");
+ }
+
+ /**
+ * Convert to a URI
+ * (such as "https://commons.wikimedia.org/wiki/File:My_example.jpg").
+ *
+ * @return URI
+ */
+ @NonNull
+ public Uri getCanonicalUri() {
+ String uriStr = CommonsApplication.HOME_URL + Uri.encode(getPrefixedText(), ":/");
+ return Uri.parse(uriStr);
+ }
+
+
+ /**
+ * Convert to a mobile URI
+ * (such as "https://commons.m.wikimedia.org/wiki/File:My_example.jpg").
+ *
+ * @return URI
+ */
+ @NonNull
+ public Uri getMobileUri() {
+ String uriStr = CommonsApplication.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
+ return Uri.parse(uriStr);
+ }
+
+ /**
+ * Get the canonical title without namespace.
+ * @return title
+ */
+ @NonNull
+ public String getText() {
+ return titleKey;
+ }
+
+ @Override
+ public String toString() {
+ return getPrefixedText();
+ }
+}
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 c44a0465a..af19a5232 100644
--- a/app/src/main/java/fr/free/nrw/commons/Utils.java
+++ b/app/src/main/java/fr/free/nrw/commons/Utils.java
@@ -1,7 +1,6 @@
package fr.free.nrw.commons;
import android.content.Context;
-import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import android.text.Html;
@@ -9,7 +8,6 @@ import android.text.Spanned;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
-import org.apache.commons.codec.net.URLCodec;
import org.w3c.dom.Node;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -20,6 +18,7 @@ import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
+import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
@@ -126,7 +125,7 @@ public class Utils {
}
public static String makeThumbBaseUrl(String filename) {
- String name = filename.replaceFirst("File:", "").replace(" ", "_");
+ String name = new PageTitle(filename).getPrefixedText();
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
return String.format("%s/%s/%s/%s", CommonsApplication.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
}
@@ -153,11 +152,9 @@ public class Utils {
return outputStream.toString();
}
- private static final URLCodec urlCodec = new URLCodec();
-
public static String urlEncode(String url) {
try {
- return urlCodec.encode(url, "utf-8");
+ return URLEncoder.encode(url, "utf-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
@@ -232,12 +229,6 @@ public class Utils {
throw new RuntimeException("Unrecognized license value: " + license);
}
- public static Uri uriForWikiPage(String name) {
- String underscored = name.trim().replace(" ", "_");
- String uriStr = CommonsApplication.HOME_URL + urlEncode(underscored);
- return Uri.parse(uriStr);
- }
-
/**
* Fast-forward an XmlPullParser to the next instance of the given element
* in the input stream (namespaced).
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 432c06c80..37a4b0e05 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
@@ -20,8 +20,9 @@ import android.widget.Toast;
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.PageTitle;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import timber.log.Timber;
@@ -162,7 +163,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
* @return String canonicial username
*/
private String canonicializeUsername( String username ) {
- return Utils.capitalize(username.substring(0,1)) + username.substring(1);
+ return new PageTitle(username).getText();
}
@Override
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 2f91afa40..8025b94bf 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
@@ -2,6 +2,7 @@ package fr.free.nrw.commons.contributions;
import android.Manifest;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
@@ -9,6 +10,7 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -105,19 +107,57 @@ public class ContributionsListFragment extends Fragment {
case R.id.menu_from_gallery:
//Gallery crashes before reach ShareActivity screen so must implement permissions check here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (ContextCompat.checkSelfPermission(this.getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- //See http://stackoverflow.com/questions/33169455/onrequestpermissionsresult-not-being-called-in-dialog-fragment
- requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
- return true;
+
+ // Here, thisActivity is the current activity
+ if (ContextCompat.checkSelfPermission(getActivity(),
+ Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ // Should we show an explanation?
+ if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) {
+
+ // Show an explanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+
+ new AlertDialog.Builder(getActivity())
+ .setMessage(getString(R.string.storage_permission_rationale))
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ requestPermissions(
+ new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ 1);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("Cancel", null)
+ .create()
+ .show();
+
+ } else {
+
+ // No explanation needed, we can request the permission.
+
+ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ 1);
+
+ // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
+ // app-defined int constant. The callback method gets the
+ // result of the request.
+ }
} else {
controller.startGalleryPick();
return true;
}
- }
- else {
+
+ } else {
controller.startGalleryPick();
return true;
}
+
+ return true;
case R.id.menu_from_camera:
controller.startCameraCapture();
return true;
@@ -128,6 +168,9 @@ public class ContributionsListFragment extends Fragment {
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+
+ Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
+
switch (requestCode) {
// 1 = Storage allowed when gallery selected
case 1: {
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/UploadCountClient.java b/app/src/main/java/fr/free/nrw/commons/contributions/UploadCountClient.java
index 2a8b54eff..9667f9057 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/UploadCountClient.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/UploadCountClient.java
@@ -9,6 +9,7 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Locale;
+import fr.free.nrw.commons.PageTitle;
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
import fr.free.nrw.commons.concurrency.ThreadPoolExecutorService;
import timber.log.Timber;
@@ -33,7 +34,8 @@ public class UploadCountClient {
public void run() {
URL url;
try {
- url = new URL(String.format(Locale.ENGLISH, UPLOAD_COUNT_URL_TEMPLATE, userName));
+ url = new URL(String.format(Locale.ENGLISH, UPLOAD_COUNT_URL_TEMPLATE,
+ new PageTitle(userName).getText()));
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
BufferedReader bufferedReader = new BufferedReader(new
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 e078a3d0e..a076a7d25 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
@@ -24,8 +24,8 @@ import fr.free.nrw.commons.LicenseList;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.MediaWikiImageView;
+import fr.free.nrw.commons.PageTitle;
import fr.free.nrw.commons.R;
-import fr.free.nrw.commons.Utils;
import timber.log.Timber;
public class MediaDetailFragment extends Fragment {
@@ -86,7 +86,7 @@ public class MediaDetailFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider)getActivity();
- if(savedInstanceState != null) {
+ if (savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable");
index = savedInstanceState.getInt("index");
initialListTop = savedInstanceState.getInt("listTop");
@@ -269,12 +269,11 @@ public class MediaDetailFragment extends Fragment {
}
}
- private View buildCatLabel(String cat) {
- final String catName = cat;
+ private View buildCatLabel(final String catName) {
final View item = getLayoutInflater(null).inflate(R.layout.detail_category_item, null, false);
final TextView textView = (TextView)item.findViewById(R.id.mediaDetailCategoryItemText);
- textView.setText(cat);
+ textView.setText(catName);
if (categoriesLoaded && categoriesPresent) {
textView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -282,7 +281,7 @@ public class MediaDetailFragment extends Fragment {
String selectedCategoryTitle = "Category:" + catName;
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
- viewIntent.setData(Utils.uriForWikiPage(selectedCategoryTitle));
+ viewIntent.setData(new PageTitle(selectedCategoryTitle).getCanonicalUri());
startActivity(viewIntent);
}
});
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 f20d32008..f684c6484 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
@@ -91,7 +91,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
final MediaDetailAdapter adapter = new MediaDetailAdapter(getChildFragmentManager());
- if(savedInstanceState != null) {
+ if (savedInstanceState != null) {
final int pageNumber = savedInstanceState.getInt("current-page");
// Adapter doesn't seem to be loading immediately.
// Dear God, please forgive us for our sins
@@ -143,7 +143,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
// View in browser
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
- viewIntent.setData(Uri.parse(m.getMobileDescriptionUrl()));
+ viewIntent.setData(m.getFilePageTitle().getMobileUri());
startActivity(viewIntent);
return true;
case R.id.menu_download_current_image:
@@ -224,14 +224,15 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.menu_share_current_image));
// On some phones null is returned for some reason:
// https://github.com/commons-app/apps-android-commons/issues/413
- if(mShareActionProvider != null) {
+ if (mShareActionProvider != null) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
- shareIntent.putExtra(Intent.EXTRA_TEXT, m.getDisplayTitle() + " \n" + m.getDescriptionUrl());
+ shareIntent.putExtra(Intent.EXTRA_TEXT,
+ m.getDisplayTitle() + " \n" + m.getFilePageTitle().getCanonicalUri());
mShareActionProvider.setShareIntent(shareIntent);
}
- if(m instanceof Contribution) {
+ if (m instanceof Contribution) {
Contribution c = (Contribution)m;
switch(c.getState()) {
case Contribution.STATE_FAILED:
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 ec7d62887..3d26b22f1 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
@@ -101,8 +101,47 @@ public class NearbyActivity extends NavigationBaseActivity {
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
startLookingForNearby();
} else {
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST);
+ if (ContextCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_FINE_LOCATION)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ // Should we show an explanation?
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.ACCESS_FINE_LOCATION)) {
+
+ // Show an explanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+
+ new AlertDialog.Builder(this)
+ .setMessage(getString(R.string.location_permission_rationale))
+ .setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ ActivityCompat.requestPermissions(NearbyActivity.this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ LOCATION_REQUEST);
+ dialog.dismiss();
+ }
+ })
+ .setNegativeButton("Cancel", null)
+ .create()
+ .show();
+
+ } else {
+
+ // No explanation needed, we can request the permission.
+
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ LOCATION_REQUEST);
+
+ // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
+ // app-defined int constant. The callback method gets the
+ // result of the request.
+ }
+ }
}
} else {
startLookingForNearby();
@@ -279,7 +318,7 @@ public class NearbyActivity extends NavigationBaseActivity {
Fragment fragment = new NearbyMapFragment();
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.container, fragment);
- fragmentTransaction.commit();
+ fragmentTransaction.commitAllowingStateLoss();
}
/**
@@ -290,7 +329,7 @@ public class NearbyActivity extends NavigationBaseActivity {
Fragment fragment = new NearbyListFragment();
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.container, fragment);
- fragmentTransaction.commit();
+ fragmentTransaction.commitAllowingStateLoss();
}
public static void startYourself(Context context) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index aad1ae7e7..d1867f3f8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -198,5 +198,6 @@ Tap this message (or hit back) to skip this step.
no description found
Commons Article
Wikidata item
+ Error while caching pictures
diff --git a/app/src/test/java/fr/free/nrw/commons/UtilsTest.java b/app/src/test/java/fr/free/nrw/commons/UtilsTest.java
index 06e26ede4..a51b7a19a 100644
--- a/app/src/test/java/fr/free/nrw/commons/UtilsTest.java
+++ b/app/src/test/java/fr/free/nrw/commons/UtilsTest.java
@@ -1,10 +1,10 @@
package fr.free.nrw.commons;
-import static org.hamcrest.CoreMatchers.is;
-
import org.junit.Assert;
import org.junit.Test;
+import static org.hamcrest.CoreMatchers.is;
+
public class UtilsTest {
@Test public void stripLocalizedStringPass() {
Assert.assertThat(Utils.stripLocalizedString("Hello"), is("Hello"));
@@ -13,4 +13,20 @@ public class UtilsTest {
@Test public void stripLocalizedStringJa() {
Assert.assertThat(Utils.stripLocalizedString("\"こんにちは\"@ja"), is("こんにちは"));
}
+
+ @Test public void capitalizeLowercase() {
+ Assert.assertThat(Utils.capitalize("hello"), is("Hello"));
+ }
+
+ @Test public void capitalizeFullCaps() {
+ Assert.assertThat(Utils.capitalize("HELLO"), is("HELLO"));
+ }
+
+ @Test public void capitalizeNumbersPass() {
+ Assert.assertThat(Utils.capitalize("12x"), is("12x"));
+ }
+
+ @Test public void capitalizeJaPass() {
+ Assert.assertThat(Utils.capitalize("こんにちは"), is("こんにちは"));
+ }
}