Merge branch 'master' into tidySettings
4
.gitignore
vendored
|
|
@ -23,3 +23,7 @@ local.properties
|
|||
# OS files
|
||||
*.DS_Store
|
||||
Thumbs.db
|
||||
app/gradle/wrapper/gradle-wrapper.jar
|
||||
app/gradlew
|
||||
app/gradlew.bat
|
||||
app/gradle/wrapper/gradle-wrapper.properties
|
||||
|
|
|
|||
6
CREDITS
|
|
@ -19,3 +19,9 @@ their contribution to the product.
|
|||
* Veyndan Stuart
|
||||
* Vivek Maskara
|
||||
* Neslihan Turan
|
||||
|
||||
3rd party open source libraries used:
|
||||
* Butterknife
|
||||
* GSON
|
||||
* Timber
|
||||
* MapBox
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ dependencies {
|
|||
compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
|
||||
annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
|
||||
compile 'com.jakewharton.timber:timber:4.5.1'
|
||||
compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.0.2@aar'){
|
||||
transitive=true
|
||||
}
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
androidTestCompile "com.android.support:support-annotations:${project.supportLibVersion}"
|
||||
|
|
|
|||
43
app/src/main/assets/queries/nearby_query.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
SELECT
|
||||
(SAMPLE(?location) as ?location)
|
||||
?item
|
||||
(SAMPLE(COALESCE(?item_label_preferred_language, ?item_label_any_language)) as ?label)
|
||||
(SAMPLE(?classId) as ?class)
|
||||
(SAMPLE(COALESCE(?class_label_preferred_language, ?class_label_any_language, "?")) as ?class_label)
|
||||
(SAMPLE(COALESCE(?icon0, ?icon1)) as ?icon)
|
||||
(SAMPLE(COALESCE(?emoji0, ?emoji1)) as ?emoji)
|
||||
(SAMPLE(?sitelink) as ?sitelink)
|
||||
WHERE {
|
||||
# Around given location...
|
||||
SERVICE wikibase:around {
|
||||
?item wdt:P625 ?location.
|
||||
bd:serviceParam wikibase:center "Point(${LONG} ${LAT})"^^geo:wktLiteral.
|
||||
bd:serviceParam wikibase:radius "${RADIUS}" . # Radius in kilometers.
|
||||
}
|
||||
|
||||
# ... and without an image.
|
||||
MINUS {?item wdt:P18 []}
|
||||
|
||||
# Get the label in the preferred language of the user, or any other language if no label is available in that language.
|
||||
OPTIONAL {?item rdfs:label ?item_label_preferred_language. FILTER (lang(?item_label_preferred_language) = "${LANG}")}
|
||||
OPTIONAL {?item rdfs:label ?item_label_any_language}
|
||||
|
||||
# Get the class label in the preferred language of the user, or any other language if no label is available in that language.
|
||||
OPTIONAL {
|
||||
?item p:P31/ps:P31 ?classId.
|
||||
OPTIONAL {?classId rdfs:label ?class_label_preferred_language. FILTER (lang(?class_label_preferred_language) = "${LANG}")}
|
||||
OPTIONAL {?classId rdfs:label ?class_label_any_language}
|
||||
|
||||
# Get icon
|
||||
OPTIONAL { ?classId wdt:P2910 ?icon0. }
|
||||
OPTIONAL { ?classId wdt:P279*/wdt:P2910 ?icon1. }
|
||||
# Get emoji
|
||||
OPTIONAL { ?classId wdt:P487 ?emoji0. }
|
||||
OPTIONAL { ?classId wdt:P279*/wdt:P487 ?emoji1. }
|
||||
OPTIONAL {
|
||||
?sitelink schema:about ?item .
|
||||
?sitelink schema:inLanguage "en"
|
||||
}
|
||||
}
|
||||
}
|
||||
GROUP BY ?item
|
||||
|
|
@ -32,7 +32,6 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
|||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.CoreProtocolPNames;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -94,9 +94,7 @@ public class EventLog {
|
|||
data.put("appversion", "Android/" + BuildConfig.VERSION_NAME);
|
||||
fullData.put("event", data);
|
||||
return new URL(CommonsApplication.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (JSONException e) {
|
||||
} catch (MalformedURLException | JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,9 +69,8 @@ public class LicenseList {
|
|||
int nameId = stringIdByName(stringId);
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: nameId: " + nameId);
|
||||
if(nameId != 0) {
|
||||
String name = res.getString(nameId);
|
||||
//Log.d("Commons", "LicenseList.nameForTemplate: name: " + name);
|
||||
return name;
|
||||
return res.getString(nameId);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
|
|
|||
64
app/src/main/java/fr/free/nrw/commons/MWApi.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.http.impl.client.AbstractHttpClient;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
|
||||
/**
|
||||
* @author Addshore
|
||||
*/
|
||||
public class MWApi extends org.mediawiki.api.MWApi {
|
||||
|
||||
public MWApi(String apiURL, AbstractHttpClient client) {
|
||||
super(apiURL, client);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param username String
|
||||
* @param password String
|
||||
* @return String On success: "PASS"
|
||||
* continue: "2FA" (More information required for 2FA)
|
||||
* failure: A failure message code (defined by mediawiki)
|
||||
* misc: genericerror-UI, genericerror-REDIRECT, genericerror-RESTART
|
||||
* @throws IOException On api request IO issue
|
||||
*/
|
||||
public String login(String username, String password) throws IOException {
|
||||
|
||||
/** Request a login token to be used later to log in. */
|
||||
ApiResult tokenData = this.action("query")
|
||||
.param("action", "query")
|
||||
.param("meta", "tokens")
|
||||
.param("type", "login")
|
||||
.post();
|
||||
String token = tokenData.getString("/api/query/tokens/@logintoken");
|
||||
|
||||
/** Actually log in. */
|
||||
ApiResult loginData = this.action("clientlogin")
|
||||
.param("rememberMe", "1")
|
||||
.param("username", username)
|
||||
.param("password", password)
|
||||
.param("logintoken", token)
|
||||
.param("loginreturnurl", "http://example.com/")//TODO return to url?
|
||||
.post();
|
||||
String status = loginData.getString("/api/clientlogin/@status");
|
||||
|
||||
if (status.equals("PASS")) {
|
||||
this.isLoggedIn = true;
|
||||
return status;
|
||||
} else if (status.equals("FAIL")) {
|
||||
return loginData.getString("/api/clientlogin/@messagecode");
|
||||
} else if (
|
||||
status.equals("UI")
|
||||
&& loginData.getString("/api/clientlogin/requests/_v/@id").equals("TOTPAuthenticationRequest")
|
||||
&& loginData.getString("/api/clientlogin/requests/_v/@provider").equals("Two-factor authentication (OATH).")
|
||||
) {
|
||||
return "2FA";
|
||||
}
|
||||
|
||||
// UI, REDIRECT, RESTART
|
||||
return "genericerror-" + status;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -144,6 +144,14 @@ public class Media implements Parcelable {
|
|||
this.license = license;
|
||||
}
|
||||
|
||||
public String getCoordinates() {
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
public void setCoordinates(String coordinates) {
|
||||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
// Primary metadata fields
|
||||
protected Uri localUri;
|
||||
protected String imageUrl;
|
||||
|
|
@ -155,6 +163,7 @@ public class Media implements Parcelable {
|
|||
protected int width;
|
||||
protected int height;
|
||||
protected String license;
|
||||
private String coordinates;
|
||||
protected String creator;
|
||||
protected ArrayList<String> categories; // as loaded at runtime?
|
||||
protected Map<String, String> descriptions; // multilingual descriptions as loaded
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
|
@ -39,6 +40,7 @@ public class MediaDataExtractor {
|
|||
private String author;
|
||||
private Date date;
|
||||
private String license;
|
||||
private String coordinates;
|
||||
private LicenseList licenseList;
|
||||
|
||||
/**
|
||||
|
|
@ -111,9 +113,7 @@ public class MediaDataExtractor {
|
|||
doc = docBuilder.parse(new ByteArrayInputStream(source.getBytes("UTF-8")));
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (IllegalStateException e) {
|
||||
throw new IOException(e);
|
||||
} catch (SAXException e) {
|
||||
} catch (IllegalStateException | SAXException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
Node templateNode = findTemplate(doc.getDocumentElement(), "information");
|
||||
|
|
@ -125,6 +125,14 @@ public class MediaDataExtractor {
|
|||
author = getFlatText(authorNode);
|
||||
}
|
||||
|
||||
Node coordinateTemplateNode = findTemplate(doc.getDocumentElement(), "location");
|
||||
|
||||
if (coordinateTemplateNode != null) {
|
||||
coordinates = getCoordinates(coordinateTemplateNode);
|
||||
} else {
|
||||
coordinates = "No coordinates found";
|
||||
}
|
||||
|
||||
/*
|
||||
Pull up the license data list...
|
||||
look for the templates in two ways:
|
||||
|
|
@ -245,6 +253,25 @@ public class MediaDataExtractor {
|
|||
return parentNode.getTextContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the coordinates from the template and returns them as pretty formatted string.
|
||||
* Loops over the children of the coordinate template:
|
||||
* {{Location|47.50111007666667|19.055700301944444}}
|
||||
* and extracts the latitude and longitude as a pretty string.
|
||||
*
|
||||
* @param parentNode The node of the coordinates template.
|
||||
* @return Pretty formatted coordinates.
|
||||
* @throws IOException Parsing failed.
|
||||
*/
|
||||
private String getCoordinates(Node parentNode) throws IOException {
|
||||
NodeList childNodes = parentNode.getChildNodes();
|
||||
double latitudeText = Double.parseDouble(childNodes.item(1).getTextContent());
|
||||
double longitudeText = Double.parseDouble(childNodes.item(2).getTextContent());
|
||||
LatLng coordinates = new LatLng(latitudeText, longitudeText);
|
||||
|
||||
return coordinates.getPrettyCoordinateString();
|
||||
}
|
||||
|
||||
// Extract a dictionary of multilingual texts from a subset of the parse tree.
|
||||
// Texts are wrapped in things like {{en|foo} or {{en|1=foo bar}}.
|
||||
// Text outside those wrappers is stuffed into a 'default' faux language key if present.
|
||||
|
|
@ -290,6 +317,7 @@ public class MediaDataExtractor {
|
|||
|
||||
media.setCategories(categories);
|
||||
media.setDescriptions(descriptions);
|
||||
media.setCoordinates(coordinates);
|
||||
if (license != null) {
|
||||
media.setLicense(license);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,10 +122,7 @@ public class Utils {
|
|||
Transformer transformer = null;
|
||||
try {
|
||||
transformer = TransformerFactory.newInstance().newTransformer();
|
||||
} catch (TransformerConfigurationException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (TransformerFactoryConfigurationError e) {
|
||||
} catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -202,56 +199,59 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static String licenseTemplateFor(String license) {
|
||||
if (license.equals(Prefs.Licenses.CC_BY_3)) {
|
||||
return "{{self|cc-by-3.0}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_4)) {
|
||||
return "{{self|cc-by-4.0}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_3)) {
|
||||
return "{{self|cc-by-sa-3.0}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_4)) {
|
||||
return "{{self|cc-by-sa-4.0}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC0)) {
|
||||
return "{{self|cc-zero}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY)) {
|
||||
return "{{self|cc-by-3.0}}";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA)) {
|
||||
return "{{self|cc-by-sa-3.0}}";
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return "{{self|cc-by-3.0}}";
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return "{{self|cc-by-4.0}}";
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return "{{self|cc-by-sa-3.0}}";
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return "{{self|cc-by-sa-4.0}}";
|
||||
case Prefs.Licenses.CC0:
|
||||
return "{{self|cc-zero}}";
|
||||
case Prefs.Licenses.CC_BY:
|
||||
return "{{self|cc-by-3.0}}";
|
||||
case Prefs.Licenses.CC_BY_SA:
|
||||
return "{{self|cc-by-sa-3.0}}";
|
||||
}
|
||||
throw new RuntimeException("Unrecognized license value");
|
||||
throw new RuntimeException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
public static int licenseNameFor(String license) {
|
||||
if (license.equals(Prefs.Licenses.CC_BY_3)) {
|
||||
return R.string.license_name_cc_by;
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_4)) {
|
||||
return R.string.license_name_cc_by_four;
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_3)) {
|
||||
return R.string.license_name_cc_by_sa;
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_4)) {
|
||||
return R.string.license_name_cc_by_sa_four;
|
||||
} else if (license.equals(Prefs.Licenses.CC0)) {
|
||||
return R.string.license_name_cc0;
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY)) { // for backward compatibility to v2.1
|
||||
return R.string.license_name_cc_by_3_0;
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA)) { // for backward compatibility to v2.1
|
||||
return R.string.license_name_cc_by_sa_3_0;
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return R.string.license_name_cc_by;
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return R.string.license_name_cc_by_four;
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return R.string.license_name_cc_by_sa;
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return R.string.license_name_cc_by_sa_four;
|
||||
case Prefs.Licenses.CC0:
|
||||
return R.string.license_name_cc0;
|
||||
case Prefs.Licenses.CC_BY: // for backward compatibility to v2.1
|
||||
return R.string.license_name_cc_by_3_0;
|
||||
case Prefs.Licenses.CC_BY_SA: // for backward compatibility to v2.1
|
||||
return R.string.license_name_cc_by_sa_3_0;
|
||||
}
|
||||
throw new RuntimeException("Unrecognized license value");
|
||||
throw new RuntimeException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
public static String licenseUrlFor(String license) {
|
||||
if (license.equals(Prefs.Licenses.CC_BY_3)) {
|
||||
return "https://creativecommons.org/licenses/by/3.0/";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_4)) {
|
||||
return "https://creativecommons.org/licenses/by/4.0/";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_3)) {
|
||||
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
||||
} else if (license.equals(Prefs.Licenses.CC_BY_SA_4)) {
|
||||
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
||||
} else if (license.equals(Prefs.Licenses.CC0)) {
|
||||
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return "https://creativecommons.org/licenses/by/3.0/";
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return "https://creativecommons.org/licenses/by/4.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
||||
case Prefs.Licenses.CC0:
|
||||
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
||||
}
|
||||
throw new RuntimeException("Unrecognized license value");
|
||||
throw new RuntimeException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
public static Uri uriForWikiPage(String name) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
.param("result", result)
|
||||
.log();
|
||||
|
||||
if (result.equals("Success")) {
|
||||
if (result.equals("PASS")) {
|
||||
if (dialog != null && dialog.isShowing()) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
|
@ -100,20 +100,29 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
|
||||
} else {
|
||||
int response;
|
||||
// Match known failure message codes and provide messages
|
||||
if(result.equals("NetworkFailure")) {
|
||||
// Matches NetworkFailure which is created by the doInBackground method
|
||||
response = R.string.login_failed_network;
|
||||
} else if(result.equals("NotExists") || result.equals("Illegal") || result.equals("NotExists")) {
|
||||
} else if (result.toLowerCase().contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
|
||||
// Matches nosuchuser, nosuchusershort, noname
|
||||
response = R.string.login_failed_username;
|
||||
passwordEdit.setText("");
|
||||
} else if(result.equals("EmptyPass") || result.equals("WrongPass") || result.equals("WrongPluginPass")) {
|
||||
|
||||
} else if (result.toLowerCase().contains("wrongpassword".toLowerCase())) {
|
||||
// Matches wrongpassword, wrongpasswordempty
|
||||
response = R.string.login_failed_password;
|
||||
passwordEdit.setText("");
|
||||
} else if(result.equals("Throttled")) {
|
||||
} else if (result.toLowerCase().contains("throttle".toLowerCase())) {
|
||||
// Matches unknown throttle error codes
|
||||
response = R.string.login_failed_throttled;
|
||||
} else if(result.equals("Blocked")) {
|
||||
} else if (result.toLowerCase().contains("userblocked".toLowerCase())) {
|
||||
// Matches login-userblocked
|
||||
response = R.string.login_failed_blocked;
|
||||
} else if (result.equals("2FA")){
|
||||
response = R.string.login_failed_2fa_not_supported;
|
||||
} else {
|
||||
// Should never really happen
|
||||
// Occurs with unhandled login failure codes
|
||||
Timber.d("Login failed with reason: %s", result);
|
||||
response = R.string.login_failed_generic;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,10 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
|
||||
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
||||
|
||||
|
|
@ -46,7 +45,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
|
|||
private String getAuthCookie(String username, String password) throws IOException {
|
||||
MWApi api = CommonsApplication.createMWApi();
|
||||
String result = api.login(username, password);
|
||||
if(result.equals("Success")) {
|
||||
if(result.equals("PASS")) {
|
||||
return api.getAuthCookie();
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class CacheController {
|
|||
|
||||
public void cacheCategory() {
|
||||
List<String> pointCatList = new ArrayList<>();
|
||||
if (MwVolleyApi.GpsCatExists.getGpsCatExists() == true) {
|
||||
if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
|
||||
pointCatList.addAll(MwVolleyApi.getGpsCat());
|
||||
Timber.d("Categories being cached: %s", pointCatList);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -357,8 +357,7 @@ public class CategorizationFragment extends Fragment {
|
|||
new String[] {name},
|
||||
null);
|
||||
if (cursor.moveToFirst()) {
|
||||
Category cat = Category.fromCursor(cursor);
|
||||
return cat;
|
||||
return Category.fromCursor(cursor);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// This feels lazy, but to hell with checked exceptions. :)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package fr.free.nrw.commons.category;
|
|||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -108,7 +108,6 @@ public class MethodAUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
|
|||
}
|
||||
|
||||
Timber.d("Found categories from Method A search, waiting for filter");
|
||||
ArrayList<String> filteredItems = new ArrayList<>(filterYears(categories));
|
||||
return filteredItems;
|
||||
return new ArrayList<>(filterYears(categories));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import android.os.AsyncTask;
|
|||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -83,16 +83,14 @@ public class PrefixUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
|
|||
if(TextUtils.isEmpty(filter)) {
|
||||
ArrayList<String> mergedItems = new ArrayList<>(catFragment.mergeItems());
|
||||
Timber.d("Merged items, waiting for filter");
|
||||
ArrayList<String> filteredItems = new ArrayList<>(filterYears(mergedItems));
|
||||
return filteredItems;
|
||||
return new ArrayList<>(filterYears(mergedItems));
|
||||
}
|
||||
|
||||
//if user types in something that is in cache, return cached category
|
||||
if(catFragment.categoriesCache.containsKey(filter)) {
|
||||
ArrayList<String> cachedItems = new ArrayList<>(catFragment.categoriesCache.get(filter));
|
||||
Timber.d("Found cache items, waiting for filter");
|
||||
ArrayList<String> filteredItems = new ArrayList<>(filterYears(cachedItems));
|
||||
return filteredItems;
|
||||
return new ArrayList<>(filterYears(cachedItems));
|
||||
}
|
||||
|
||||
//otherwise if user has typed something in that isn't in cache, search API for matching categories
|
||||
|
|
@ -119,7 +117,6 @@ public class PrefixUpdater extends AsyncTask<Void, Void, ArrayList<String>> {
|
|||
}
|
||||
|
||||
Timber.d("Found categories from Prefix search, waiting for filter");
|
||||
ArrayList<String> filteredItems = new ArrayList<>(filterYears(categories));
|
||||
return filteredItems;
|
||||
return new ArrayList<>(filterYears(categories));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ package fr.free.nrw.commons.category;
|
|||
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ public class Contribution extends Media {
|
|||
cv.put(Table.COLUMN_LOCAL_URI, getLocalUri().toString());
|
||||
}
|
||||
if(getImageUrl() != null) {
|
||||
cv.put(Table.COLUMN_IMAGE_URL, getImageUrl().toString());
|
||||
cv.put(Table.COLUMN_IMAGE_URL, getImageUrl());
|
||||
}
|
||||
if(getDateUploaded() != null) {
|
||||
cv.put(Table.COLUMN_UPLOADED, getDateUploaded().getTime());
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class ContributionController {
|
|||
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Commons/images/" + new Date().getTime() + ".jpg";
|
||||
File _photoFile = new File(path);
|
||||
try {
|
||||
if(_photoFile.exists() == false) {
|
||||
if(!_photoFile.exists()) {
|
||||
_photoFile.getParentFile().mkdirs();
|
||||
_photoFile.createNewFile();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import android.content.ContentResolver;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.Cursor;
|
||||
import android.database.DataSetObserver;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
|
|
@ -21,6 +23,7 @@ import android.widget.Adapter;
|
|||
import android.widget.AdapterView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.HandlerService;
|
||||
|
|
@ -29,6 +32,7 @@ import fr.free.nrw.commons.R;
|
|||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||
import fr.free.nrw.commons.auth.WikiAccountAuthenticator;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.UploadService;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
@ -38,7 +42,8 @@ public class ContributionsActivity
|
|||
AdapterView.OnItemClickListener,
|
||||
MediaDetailPagerFragment.MediaDetailProvider,
|
||||
FragmentManager.OnBackStackChangedListener,
|
||||
ContributionsListFragment.SourceRefresher {
|
||||
ContributionsListFragment.SourceRefresher
|
||||
{
|
||||
|
||||
private Cursor allContributions;
|
||||
private ContributionsListFragment contributionsList;
|
||||
|
|
@ -205,7 +210,12 @@ public class ContributionsActivity
|
|||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
|
||||
return new CursorLoader(this, ContributionsContentProvider.BASE_URI, Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
|
||||
SharedPreferences sharedPref =
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
|
||||
return new CursorLoader(this, ContributionsContentProvider.BASE_URI,
|
||||
Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null,
|
||||
CONTRIBUTION_SORT + "LIMIT " + uploads);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -216,7 +226,15 @@ public class ContributionsActivity
|
|||
((CursorAdapter)contributionsList.getAdapter()).swapCursor(cursor);
|
||||
}
|
||||
|
||||
getSupportActionBar().setSubtitle(getResources().getQuantityString(R.plurals.contributions_subtitle, cursor.getCount(), cursor.getCount()));
|
||||
if (cursor.getCount() == 0
|
||||
&& Locale.getDefault().getISO3Language().equals(Locale.ENGLISH.getISO3Language())) {
|
||||
//cursor count is zero and language is english -
|
||||
// we need to set the message for 0 case explicitly.
|
||||
getSupportActionBar().setSubtitle(getResources()
|
||||
.getString(R.string.contributions_subtitle_zero));
|
||||
} else {
|
||||
getSupportActionBar().setSubtitle(getResources().getQuantityString(R.plurals.contributions_subtitle, cursor.getCount(), cursor.getCount()));
|
||||
}
|
||||
|
||||
contributionsList.clearSyncMessage();
|
||||
notifyAndMigrateDataSetObservers();
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import android.os.Bundle;
|
|||
import android.os.RemoteException;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -30,7 +30,10 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
}
|
||||
|
||||
private int getLimit() {
|
||||
return 500; // FIXME: Parameterize!
|
||||
|
||||
int limit = 100;
|
||||
Timber.d("Max number of uploads set to %d", limit);
|
||||
return limit; // FIXME: Parameterize!
|
||||
}
|
||||
|
||||
private static final String[] existsQuery = { Contribution.Table.COLUMN_FILENAME };
|
||||
|
|
@ -68,6 +71,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
|
|||
while(!done) {
|
||||
|
||||
try {
|
||||
Timber.d("Example API query: https://commons.wikimedia.org/w/api.php?action=query&list=logevents&letype=upload&leuser=Sandaru&lelimit=5");
|
||||
MWApi.RequestBuilder builder = api.action("query")
|
||||
.param("list", "logevents")
|
||||
.param("letype", "upload")
|
||||
|
|
|
|||
|
|
@ -42,4 +42,52 @@ public class LatLng {
|
|||
public String toString() {
|
||||
return "lat/lng: (" + this.latitude + "," + this.longitude + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the float to 4 digits.
|
||||
*
|
||||
* @param coordinate A coordinate value as string.
|
||||
* @return String of the rounded number.
|
||||
*/
|
||||
private String formatCoordinate(double coordinate) {
|
||||
double roundedNumber = Math.round(coordinate * 10000d) / 10000d;
|
||||
return String.valueOf(roundedNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "N" or "S" depending on the latitude.
|
||||
*
|
||||
* @return "N" or "S".
|
||||
*/
|
||||
private String getNorthSouth() {
|
||||
if (this.latitude < 0) {
|
||||
return "S";
|
||||
}
|
||||
|
||||
return "N";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "E" or "W" depending on the longitude.
|
||||
*
|
||||
* @return "E" or "W".
|
||||
*/
|
||||
private String getEastWest() {
|
||||
if (this.longitude < 180) {
|
||||
return "E";
|
||||
}
|
||||
|
||||
return "W";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nicely formatted coordinate string. Used e.g. in
|
||||
* the detail view.
|
||||
*
|
||||
* @return The formatted string.
|
||||
*/
|
||||
public String getPrettyCoordinateString() {
|
||||
return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", "
|
||||
+ formatCoordinate(this.longitude) + " " + this.getEastWest();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ public class MediaDetailFragment extends Fragment {
|
|||
private TextView title;
|
||||
private TextView desc;
|
||||
private TextView license;
|
||||
private TextView coordinates;
|
||||
private LinearLayout categoryContainer;
|
||||
private ScrollView scrollView;
|
||||
private ArrayList<String> categoryNames;
|
||||
|
|
@ -123,6 +124,7 @@ public class MediaDetailFragment extends Fragment {
|
|||
title = (TextView) view.findViewById(R.id.mediaDetailTitle);
|
||||
desc = (TextView) view.findViewById(R.id.mediaDetailDesc);
|
||||
license = (TextView) view.findViewById(R.id.mediaDetailLicense);
|
||||
coordinates = (TextView) view.findViewById(R.id.mediaDetailCoordinates);
|
||||
categoryContainer = (LinearLayout) view.findViewById(R.id.mediaDetailCategoryContainer);
|
||||
|
||||
licenseList = new LicenseList(getActivity());
|
||||
|
|
@ -220,12 +222,13 @@ public class MediaDetailFragment extends Fragment {
|
|||
protected void onPostExecute(Boolean success) {
|
||||
detailFetchTask = null;
|
||||
|
||||
if (success.booleanValue()) {
|
||||
if (success) {
|
||||
extractor.fill(media);
|
||||
|
||||
// Set text of desc, license, and categories
|
||||
desc.setText(prettyDescription(media));
|
||||
license.setText(prettyLicense(media));
|
||||
coordinates.setText(prettyCoordinates(media));
|
||||
|
||||
categoryNames.removeAll(categoryNames);
|
||||
categoryNames.addAll(media.getCategories());
|
||||
|
|
@ -388,4 +391,15 @@ public class MediaDetailFragment extends Fragment {
|
|||
return licenseObj.getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coordinates nicely formatted.
|
||||
*
|
||||
* @return Coordinates as text.
|
||||
*/
|
||||
private String prettyCoordinates(Media media) {
|
||||
String coordinates = media.getCoordinates();
|
||||
|
||||
return coordinates;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ import android.database.Cursor;
|
|||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,58 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.UriSerializer;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NearbyActivity extends BaseActivity {
|
||||
@BindView(R.id.progressBar)
|
||||
ProgressBar progressBar;
|
||||
private boolean isMapViewActive = false;
|
||||
|
||||
private LocationServiceManager locationManager;
|
||||
private LatLng curLatLang;
|
||||
private Gson gson;
|
||||
private String gsonPlaceList;
|
||||
private String gsonCurLatLng;
|
||||
private Bundle bundle;
|
||||
private NearbyAsyncTask nearbyAsyncTask;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_nearby);
|
||||
ButterKnife.bind(this);
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
bundle = new Bundle();
|
||||
locationManager = new LocationServiceManager(this);
|
||||
locationManager.registerLocationManager();
|
||||
curLatLang = locationManager.getLatestLocation();
|
||||
nearbyAsyncTask = new NearbyAsyncTask();
|
||||
nearbyAsyncTask.execute();
|
||||
|
||||
// Begin the transaction
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
NearbyListFragment fragment = new NearbyListFragment();
|
||||
ft.add(R.id.container, fragment);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -45,19 +69,31 @@ public class NearbyActivity extends BaseActivity {
|
|||
case R.id.action_refresh:
|
||||
refreshView();
|
||||
return true;
|
||||
case R.id.action_map:
|
||||
showMapView();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void showMapView() {
|
||||
if (!isMapViewActive) {
|
||||
isMapViewActive = true;
|
||||
if (nearbyAsyncTask.getStatus() == AsyncTask.Status.FINISHED) {
|
||||
setMapFragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
protected void refreshView() {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.container, new NearbyListFragment()).commit();
|
||||
nearbyAsyncTask = new NearbyAsyncTask();
|
||||
nearbyAsyncTask.execute();
|
||||
}
|
||||
|
||||
public LocationServiceManager getLocationManager() {
|
||||
|
|
@ -69,4 +105,77 @@ public class NearbyActivity extends BaseActivity {
|
|||
super.onDestroy();
|
||||
locationManager.unregisterLocationManager();
|
||||
}
|
||||
|
||||
private class NearbyAsyncTask extends AsyncTask<Void, Integer, List<Place>> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
super.onProgressUpdate(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Place> doInBackground(Void... params) {
|
||||
return NearbyController
|
||||
.loadAttractionsFromLocation(curLatLang, getApplicationContext()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<Place> placeList) {
|
||||
super.onPostExecute(placeList);
|
||||
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.create();
|
||||
gsonPlaceList = gson.toJson(placeList);
|
||||
gsonCurLatLng = gson.toJson(curLatLang);
|
||||
|
||||
bundle.clear();
|
||||
bundle.putString("PlaceList", gsonPlaceList);
|
||||
bundle.putString("CurLatLng", gsonCurLatLng);
|
||||
|
||||
// Begin the transaction
|
||||
if (isMapViewActive) {
|
||||
setMapFragment();
|
||||
} else {
|
||||
setListFragment();
|
||||
}
|
||||
|
||||
if (progressBar != null) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls fragment for map view.
|
||||
*/
|
||||
public void setMapFragment() {
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
NearbyMapFragment fragment = new NearbyMapFragment();
|
||||
fragment.setArguments(bundle);
|
||||
ft.add(R.id.container, fragment);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls fragment for list view.
|
||||
*/
|
||||
public void setListFragment() {
|
||||
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
|
||||
NearbyListFragment fragment = new NearbyListFragment();
|
||||
fragment.setArguments(bundle);
|
||||
ft.add(R.id.container, fragment);
|
||||
ft.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.view.ViewGroup;
|
|||
import android.widget.ArrayAdapter;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NearbyAdapter extends ArrayAdapter<Place> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
|
||||
import com.mapbox.mapboxsdk.annotations.Icon;
|
||||
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
import fr.free.nrw.commons.utils.UriSerializer;
|
||||
|
||||
public class NearbyBaseMarker extends BaseMarkerOptions<NearbyMarker, NearbyBaseMarker> {
|
||||
private Place place;
|
||||
public NearbyBaseMarker() {
|
||||
|
||||
}
|
||||
|
||||
public NearbyBaseMarker place(Place place) {
|
||||
this.place = place;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public NearbyBaseMarker(Parcel in) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
|
||||
position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
|
||||
snippet(in.readString());
|
||||
String iconId = in.readString();
|
||||
Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
Icon icon = IconFactory.recreate(iconId, iconBitmap);
|
||||
icon(icon);
|
||||
title(in.readString());
|
||||
String gsonString = in.readString();
|
||||
place(gson.fromJson(gsonString, Place.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearbyBaseMarker getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NearbyMarker getMarker() {
|
||||
return new NearbyMarker(this, place);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.create();
|
||||
|
||||
dest.writeParcelable(position, flags);
|
||||
dest.writeString(snippet);
|
||||
dest.writeString(icon.getId());
|
||||
dest.writeParcelable(icon.getBitmap(), flags);
|
||||
dest.writeString(title);
|
||||
dest.writeString(gson.toJson(place));
|
||||
}
|
||||
|
||||
public Place getPlace() {
|
||||
return place;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<NearbyBaseMarker> CREATOR
|
||||
= new Parcelable.Creator<NearbyBaseMarker>() {
|
||||
public NearbyBaseMarker createFromParcel(Parcel in) {
|
||||
return new NearbyBaseMarker(in);
|
||||
}
|
||||
|
||||
public NearbyBaseMarker[] newArray(int size) {
|
||||
return new NearbyBaseMarker[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween;
|
||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||
|
||||
|
||||
public class NearbyController {
|
||||
private static final int MAX_RESULTS = 1000;
|
||||
|
||||
/**
|
||||
* Prepares Place list to make their distance information update later.
|
||||
* @param curLatLng current location for user
|
||||
* @param context context
|
||||
* @return Place list without distance information
|
||||
*/
|
||||
public static List<Place> loadAttractionsFromLocation(LatLng curLatLng, Context context) {
|
||||
Timber.d("Loading attractions near %s", curLatLng);
|
||||
if (curLatLng == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
List<Place> places = prefs.getBoolean("useWikidata", true)
|
||||
? NearbyPlaces.getInstance().getFromWikidataQuery(
|
||||
context,
|
||||
curLatLng,
|
||||
Locale.getDefault().getLanguage())
|
||||
: NearbyPlaces.getInstance().getFromWikiNeedsPictures();
|
||||
if (curLatLng != null) {
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place: places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
}
|
||||
Collections.sort(places,
|
||||
new Comparator<Place>() {
|
||||
@Override
|
||||
public int compare(Place lhs, Place rhs) {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return places;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads attractions from location for list view, we need to return Place data type.
|
||||
* @param curLatLng users current location
|
||||
* @param placeList list of nearby places in Place data type
|
||||
* @return Place list that holds nearby places
|
||||
*/
|
||||
public static List<Place> loadAttractionsFromLocationToPlaces(
|
||||
LatLng curLatLng,
|
||||
List<Place> placeList) {
|
||||
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));
|
||||
for (Place place: placeList) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
}
|
||||
return placeList;
|
||||
}
|
||||
|
||||
/**
|
||||
*Loads attractions from location for map view, we need to return BaseMarkerOption data type.
|
||||
* @param curLatLng users current location
|
||||
* @param placeList list of nearby places in Place data type
|
||||
* @return BaseMarkerOprions list that holds nearby places
|
||||
*/
|
||||
public static List<NearbyBaseMarker> loadAttractionsFromLocationToBaseMarkerOptions(
|
||||
LatLng curLatLng,
|
||||
List<Place> placeList) {
|
||||
List<NearbyBaseMarker> baseMarkerOptionses = new ArrayList<>();
|
||||
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));
|
||||
for (Place place: placeList) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
|
||||
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
|
||||
nearbyBaseMarker.title(place.name);
|
||||
nearbyBaseMarker.position(
|
||||
new com.mapbox.mapboxsdk.geometry.LatLng(
|
||||
place.location.latitude,
|
||||
place.location.longitude));
|
||||
nearbyBaseMarker.place(place);
|
||||
|
||||
baseMarkerOptionses.add(nearbyBaseMarker);
|
||||
}
|
||||
return baseMarkerOptionses;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.ui.widget.OverlayDialog;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
|
||||
public class NearbyInfoDialog extends OverlayDialog {
|
||||
|
||||
private final static String ARG_TITLE = "placeTitle";
|
||||
private final static String ARG_DESC = "placeDesc";
|
||||
private final static String ARG_LATITUDE = "latitude";
|
||||
private final static String ARG_LONGITUDE = "longitude";
|
||||
private final static String ARG_ARTICLE_LINK = "articleLink";
|
||||
private final static String ARG_WIKI_DATA_LINK = "wikiDataLink";
|
||||
|
||||
@BindView(R.id.link_preview_title)
|
||||
TextView placeTitle;
|
||||
@BindView(R.id.link_preview_extract)
|
||||
TextView placeDescription;
|
||||
|
||||
@BindView(R.id.link_preview_go_button)
|
||||
TextView goToButton;
|
||||
|
||||
private Unbinder unbinder;
|
||||
|
||||
private LatLng location;
|
||||
private Uri articleLink;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_nearby_info, container, false);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
initUi();
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initUi() {
|
||||
Bundle bundle = getArguments();
|
||||
placeTitle.setText(bundle.getString(ARG_TITLE));
|
||||
placeDescription.setText(bundle.getString(ARG_DESC));
|
||||
location = new LatLng(bundle.getDouble(ARG_LATITUDE), bundle.getDouble(ARG_LONGITUDE));
|
||||
getArticleLink(bundle);
|
||||
}
|
||||
|
||||
private void getArticleLink(Bundle bundle) {
|
||||
String articleLink = bundle.getString(ARG_ARTICLE_LINK);
|
||||
articleLink = articleLink.replace("<", "").replace(">", "");
|
||||
|
||||
if (Utils.isNullOrWhiteSpace(articleLink) || articleLink == "\n") {
|
||||
articleLink = bundle.getString(ARG_WIKI_DATA_LINK).replace("<", "").replace(">", "");
|
||||
}
|
||||
|
||||
if (!Utils.isNullOrWhiteSpace(articleLink) && articleLink != "\n") {
|
||||
this.articleLink = Uri.parse(articleLink);
|
||||
} else {
|
||||
goToButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public static void showYourself(FragmentActivity fragmentActivity, Place place) {
|
||||
NearbyInfoDialog mDialog = new NearbyInfoDialog();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(ARG_TITLE, place.name);
|
||||
bundle.putString(ARG_DESC, place.description);
|
||||
bundle.putDouble(ARG_LATITUDE, place.location.latitude);
|
||||
bundle.putDouble(ARG_LONGITUDE, place.location.longitude);
|
||||
bundle.putString(ARG_ARTICLE_LINK, place.siteLink.toString());
|
||||
bundle.putString(ARG_WIKI_DATA_LINK, place.wikiDataLink.toString());
|
||||
mDialog.setArguments(bundle);
|
||||
DialogUtil.showSafely(fragmentActivity, mDialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
@OnClick(R.id.link_preview_directions_button)
|
||||
void onDirectionsClick() {
|
||||
//Open map app at given position
|
||||
Uri gmmIntentUri = Uri.parse("geo:0,0?q=" + location.latitude + "," + location.longitude);
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
|
||||
|
||||
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(mapIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.link_preview_go_button)
|
||||
void onReadArticleClick() {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, articleLink);
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +1,39 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween;
|
||||
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnItemClick;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NearbyListFragment extends ListFragment {
|
||||
|
||||
private static final int MAX_RESULTS = 1000;
|
||||
private NearbyAsyncTask nearbyAsyncTask;
|
||||
private Gson gson;
|
||||
private List<Place> placeList;
|
||||
private LatLng curLatLng;
|
||||
|
||||
@BindView(R.id.listView) ListView listview;
|
||||
@BindView(R.id.progressBar) ProgressBar progressBar;
|
||||
|
||||
|
||||
private NearbyAdapter adapter;
|
||||
|
||||
|
|
@ -67,83 +63,27 @@ public class NearbyListFragment extends ListFragment {
|
|||
|
||||
// Check that this is the first time view is created,
|
||||
// to avoid double list when screen orientation changed
|
||||
Bundle bundle = this.getArguments();
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
if (bundle != null) {
|
||||
String gsonPlaceList = bundle.getString("PlaceList");
|
||||
String gsonLatLng = bundle.getString("CurLatLng");
|
||||
Type listType = new TypeToken<List<Place>>() {}.getType();
|
||||
placeList = gson.fromJson(gsonPlaceList, listType);
|
||||
Type curLatLngType = new TypeToken<LatLng>() {}.getType();
|
||||
curLatLng = gson.fromJson(gsonLatLng, curLatLngType);
|
||||
placeList = NearbyController.loadAttractionsFromLocationToPlaces(curLatLng, placeList);
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
adapter.clear();
|
||||
nearbyAsyncTask = new NearbyAsyncTask();
|
||||
nearbyAsyncTask.execute();
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
Timber.d("Saved instance state is null, populating ListView");
|
||||
} else {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// If we are returning here from a screen orientation and the AsyncTask is still working,
|
||||
// re-create and display the progress dialog.
|
||||
if (isTaskRunning()) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTaskRunning() {
|
||||
return nearbyAsyncTask != null && nearbyAsyncTask.getStatus() != AsyncTask.Status.FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
// All dialogs should be closed before leaving the activity in order to avoid
|
||||
// the: Activity has leaked window com.android.internal.policy... exception
|
||||
if (progressBar != null && progressBar.isShown()) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
super.onDetach();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// See http://stackoverflow.com/questions/18264408/incomplete-asynctask-crashes-my-app
|
||||
if (isTaskRunning()) {
|
||||
nearbyAsyncTask.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
private class NearbyAsyncTask extends AsyncTask<Void, Integer, List<Place>> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values) {
|
||||
super.onProgressUpdate(values);
|
||||
progressBar.setProgress(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Place> doInBackground(Void... params) {
|
||||
return loadAttractionsFromLocation(
|
||||
((NearbyActivity)getActivity()).getLocationManager().getLatestLocation()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(List<Place> places) {
|
||||
super.onPostExecute(places);
|
||||
|
||||
if (isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressBar != null) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
adapter.clear();
|
||||
adapter.addAll(places);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
adapter.clear();
|
||||
adapter.addAll(placeList);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@OnItemClick(R.id.listView)
|
||||
|
|
@ -156,48 +96,6 @@ public class NearbyListFragment extends ListFragment {
|
|||
|
||||
Timber.d("Item at position %d has coords: Lat: %f Long: %f", position, latitude, longitude);
|
||||
|
||||
//Open map app at given position
|
||||
Uri gmmIntentUri = Uri.parse("geo:0,0?q=" + latitude + "," + longitude);
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
|
||||
|
||||
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(mapIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Place> loadAttractionsFromLocation(LatLng curLatLng) {
|
||||
Timber.d("Loading attractions near %s", curLatLng);
|
||||
if (curLatLng == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
List<Place> places = prefs.getBoolean("useWikidata", true)
|
||||
? NearbyPlaces.getInstance().getFromWikidataQuery(
|
||||
curLatLng, Locale.getDefault().getLanguage())
|
||||
: NearbyPlaces.getInstance().getFromWikiNeedsPictures();
|
||||
if (curLatLng != null) {
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place: places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
}
|
||||
Collections.sort(places,
|
||||
new Comparator<Place>() {
|
||||
@Override
|
||||
public int compare(Place lhs, Place rhs) {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
places = places.subList(0, Math.min(places.size(), MAX_RESULTS));
|
||||
for (Place place: places) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
}
|
||||
return places;
|
||||
NearbyInfoDialog.showYourself(getActivity(), place);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,142 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
|
||||
public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
||||
private MapView mapView;
|
||||
private Gson gson;
|
||||
private List<Place> placeList;
|
||||
private List<NearbyBaseMarker> baseMarkerOptionses;
|
||||
private fr.free.nrw.commons.location.LatLng curLatLng;
|
||||
|
||||
public NearbyMapFragment() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
Bundle bundle = this.getArguments();
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
if (bundle != null) {
|
||||
String gsonPlaceList = bundle.getString("PlaceList");
|
||||
String gsonLatLng = bundle.getString("CurLatLng");
|
||||
Type listType = new TypeToken<List<Place>>() {}.getType();
|
||||
placeList = gson.fromJson(gsonPlaceList, listType);
|
||||
Type curLatLngType = new TypeToken<fr.free.nrw.commons.location.LatLng>() {}.getType();
|
||||
curLatLng = gson.fromJson(gsonLatLng, curLatLngType);
|
||||
baseMarkerOptionses = NearbyController
|
||||
.loadAttractionsFromLocationToBaseMarkerOptions(curLatLng, placeList);
|
||||
|
||||
}
|
||||
Mapbox.getInstance(getActivity(),
|
||||
getString(R.string.mapbox_commons_app_token));
|
||||
MapboxTelemetry.getInstance().setTelemetryEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
MapboxMapOptions options = new MapboxMapOptions()
|
||||
.styleUrl(Style.OUTDOORS)
|
||||
.camera(new CameraPosition.Builder()
|
||||
.target(new LatLng(curLatLng.latitude, curLatLng.longitude))
|
||||
.zoom(11)
|
||||
.build());
|
||||
|
||||
// create map
|
||||
mapView = new MapView(getActivity(), options);
|
||||
mapView.onCreate(savedInstanceState);
|
||||
mapView.getMapAsync(new OnMapReadyCallback() {
|
||||
@Override
|
||||
public void onMapReady(MapboxMap mapboxMap) {
|
||||
mapboxMap.addMarkers(baseMarkerOptionses);
|
||||
|
||||
mapboxMap.setOnMarkerClickListener(new MapboxMap.OnMarkerClickListener() {
|
||||
@Override
|
||||
public boolean onMarkerClick(@NonNull Marker marker) {
|
||||
if (marker instanceof NearbyMarker) {
|
||||
NearbyMarker nearbyMarker = (NearbyMarker) marker;
|
||||
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
|
||||
NearbyInfoDialog.showYourself(getActivity(), place);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",true)) {
|
||||
mapView.setStyleUrl(getResources().getString(R.string.map_theme_dark));
|
||||
} else {
|
||||
mapView.setStyleUrl(getResources().getString(R.string.map_theme_light));
|
||||
}
|
||||
|
||||
setHasOptionsMenu(false);
|
||||
|
||||
return mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
mapView.onStart();
|
||||
super.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
mapView.onPause();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
mapView.onResume();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
mapView.onStop();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
mapView.onDestroy();
|
||||
super.onDestroyView();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
|
||||
public class NearbyMarker extends Marker {
|
||||
private Place place;
|
||||
private NearbyBaseMarker nearbyBaseMarker;
|
||||
|
||||
/**
|
||||
* Creates a instance of {@link Marker} using the builder of Marker.
|
||||
*
|
||||
* @param baseMarkerOptions The builder used to construct the Marker.
|
||||
*/
|
||||
public NearbyMarker(NearbyBaseMarker baseMarkerOptions, Place place) {
|
||||
super(baseMarkerOptions);
|
||||
this.place = place;
|
||||
this.nearbyBaseMarker = baseMarkerOptions;
|
||||
}
|
||||
|
||||
public NearbyBaseMarker getNearbyBaseMarker() {
|
||||
return nearbyBaseMarker;
|
||||
}
|
||||
|
||||
public Place getPlace() {
|
||||
return place;
|
||||
}
|
||||
|
||||
public void setNearbyBaseMarker(NearbyBaseMarker nearbyBaseMarker) {
|
||||
this.nearbyBaseMarker = nearbyBaseMarker;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.StrictMode;
|
||||
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
|
@ -19,6 +17,9 @@ import java.util.Locale;
|
|||
import java.util.regex.Matcher;
|
||||
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 {
|
||||
|
|
@ -28,44 +29,6 @@ public class NearbyPlaces {
|
|||
private static final double MAX_RADIUS = 300.0;
|
||||
private static final double RADIUS_MULTIPLIER = 1.618;
|
||||
private static final String WIKIDATA_QUERY_URL = "https://query.wikidata.org/sparql?query=${QUERY}";
|
||||
private static final String WIKIDATA_QUERY_TEMPLATE = "SELECT\n" +
|
||||
" (SAMPLE(?location) as ?location)\n" +
|
||||
" ?item\n" +
|
||||
" (SAMPLE(COALESCE(?item_label_preferred_language, ?item_label_any_language)) as ?label)\n" +
|
||||
" (SAMPLE(?classId) as ?class)\n" +
|
||||
" (SAMPLE(COALESCE(?class_label_preferred_language, ?class_label_any_language, \"?\")) as ?class_label)\n" +
|
||||
" (SAMPLE(COALESCE(?icon0, ?icon1)) as ?icon)\n" +
|
||||
" (SAMPLE(COALESCE(?emoji0, ?emoji1)) as ?emoji)\n" +
|
||||
"WHERE {\n" +
|
||||
" # Around given location...\n" +
|
||||
" SERVICE wikibase:around {\n" +
|
||||
" ?item wdt:P625 ?location.\n" +
|
||||
" bd:serviceParam wikibase:center \"Point(${LONG} ${LAT})\"^^geo:wktLiteral. \n" +
|
||||
" bd:serviceParam wikibase:radius \"${RADIUS}\" . # Radius in kilometers.\n" +
|
||||
" }\n" +
|
||||
" \n" +
|
||||
" # ... and without an image.\n" +
|
||||
" MINUS {?item wdt:P18 []}\n" +
|
||||
" \n" +
|
||||
" # Get the label in the preferred language of the user, or any other language if no label is available in that language.\n" +
|
||||
" OPTIONAL {?item rdfs:label ?item_label_preferred_language. FILTER (lang(?item_label_preferred_language) = \"${LANG}\")}\n" +
|
||||
" OPTIONAL {?item rdfs:label ?item_label_any_language}\n" +
|
||||
" \n" +
|
||||
" # Get the class label in the preferred language of the user, or any other language if no label is available in that language.\n" +
|
||||
" OPTIONAL {\n" +
|
||||
" ?item p:P31/ps:P31 ?classId.\n" +
|
||||
" OPTIONAL {?classId rdfs:label ?class_label_preferred_language. FILTER (lang(?class_label_preferred_language) = \"${LANG}\")}\n" +
|
||||
" OPTIONAL {?classId rdfs:label ?class_label_any_language}\n" +
|
||||
"\n" +
|
||||
" # Get icon\n" +
|
||||
" OPTIONAL { ?classId wdt:P2910 ?icon0. }\n" +
|
||||
" OPTIONAL { ?classId wdt:P279*/wdt:P2910 ?icon1. }\n" +
|
||||
" # Get emoji\n" +
|
||||
" OPTIONAL { ?classId wdt:P487 ?emoji0. }\n" +
|
||||
" OPTIONAL { ?classId wdt:P279*/wdt:P487 ?emoji1. }\n" +
|
||||
" }\n" +
|
||||
"}\n" +
|
||||
"GROUP BY ?item\n";
|
||||
private static NearbyPlaces singleton;
|
||||
private double radius = INITIAL_RADIUS;
|
||||
private List<Place> places;
|
||||
|
|
@ -73,13 +36,15 @@ public class NearbyPlaces {
|
|||
private NearbyPlaces(){
|
||||
}
|
||||
|
||||
List<Place> getFromWikidataQuery(LatLng curLatLng, String lang) {
|
||||
List<Place> getFromWikidataQuery(Context context,
|
||||
LatLng curLatLng,
|
||||
String lang) {
|
||||
List<Place> 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);
|
||||
places = getFromWikidataQuery(context, curLatLng, lang, radius);
|
||||
Timber.d("%d results at radius: %f", places.size(), radius);
|
||||
if (places.size() >= MIN_RESULTS) {
|
||||
break;
|
||||
|
|
@ -97,10 +62,18 @@ public class NearbyPlaces {
|
|||
return places;
|
||||
}
|
||||
|
||||
private List<Place> getFromWikidataQuery(LatLng cur, String lang, double radius)
|
||||
private List<Place> getFromWikidataQuery(Context context,
|
||||
LatLng cur,
|
||||
String lang,
|
||||
double radius)
|
||||
throws IOException {
|
||||
List<Place> places = new ArrayList<>();
|
||||
String query = WIKIDATA_QUERY_TEMPLATE.replace("${RADIUS}", "" + radius)
|
||||
|
||||
String query = FileUtils.readFromFile(context, "queries/nearby_query.txt");
|
||||
|
||||
Timber.d(query);
|
||||
|
||||
query = query.replace("${RADIUS}", "" + radius)
|
||||
.replace("${LAT}", "" + String.format(Locale.ROOT, "%.3f", cur.latitude))
|
||||
.replace("${LONG}", "" + String.format(Locale.ROOT, "%.3f", cur.longitude))
|
||||
.replace("${LANG}", "" + lang);
|
||||
|
|
@ -124,6 +97,8 @@ public class NearbyPlaces {
|
|||
String point = fields[0];
|
||||
String name = Utils.stripLocalizedString(fields[2]);
|
||||
String type = Utils.stripLocalizedString(fields[4]);
|
||||
String sitelink = Utils.stripLocalizedString(fields[7]);
|
||||
String wikiDataLink = Utils.stripLocalizedString(fields[3]);
|
||||
String icon = fields[5];
|
||||
|
||||
double latitude = 0;
|
||||
|
|
@ -145,7 +120,9 @@ public class NearbyPlaces {
|
|||
type, // list
|
||||
type, // details
|
||||
Uri.parse(icon),
|
||||
new LatLng(latitude, longitude)
|
||||
new LatLng(latitude, longitude),
|
||||
Uri.parse(sitelink),
|
||||
Uri.parse(wikiDataLink)
|
||||
));
|
||||
}
|
||||
in.close();
|
||||
|
|
@ -202,7 +179,9 @@ public class NearbyPlaces {
|
|||
type, // list
|
||||
type, // details
|
||||
null,
|
||||
new LatLng(latitude, longitude)
|
||||
new LatLng(latitude, longitude),
|
||||
null,
|
||||
null
|
||||
));
|
||||
}
|
||||
in.close();
|
||||
|
|
|
|||
|
|
@ -16,14 +16,19 @@ public class Place {
|
|||
public Bitmap image;
|
||||
public Bitmap secondaryImage;
|
||||
public String distance;
|
||||
public Uri siteLink;
|
||||
public Uri wikiDataLink;
|
||||
|
||||
|
||||
public Place(String name, String description, String longDescription,
|
||||
Uri secondaryImageUrl, LatLng location) {
|
||||
Uri secondaryImageUrl, LatLng location, Uri siteLink, Uri wikiDataLink) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.longDescription = longDescription;
|
||||
this.secondaryImageUrl = secondaryImageUrl;
|
||||
this.location = location;
|
||||
this.siteLink = siteLink;
|
||||
this.wikiDataLink = wikiDataLink;
|
||||
}
|
||||
|
||||
public void setDistance(String distance) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ public class Prefs {
|
|||
|
||||
public static String TRACKING_ENABLED = "eventLogging";
|
||||
public static final String DEFAULT_LICENSE = "defaultLicense";
|
||||
public static final String UPLOADS_SHOWING = "uploadsshowing";
|
||||
|
||||
public static class Licenses {
|
||||
public static final String CC_BY_SA_3 = "CC BY-SA 3.0";
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.os.Bundle;
|
|||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
|
|
@ -34,5 +35,20 @@ public class SettingsActivity extends PreferenceActivity {
|
|||
settingsDelegate = AppCompatDelegate.create(this, null);
|
||||
}
|
||||
settingsDelegate.onPostCreate(savedInstanceState);
|
||||
|
||||
//Get an up button
|
||||
settingsDelegate.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
//Handle action-bar clicks
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
package fr.free.nrw.commons.settings;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
|
|
@ -20,8 +25,6 @@ public class SettingsFragment extends PreferenceFragment {
|
|||
// Update spinner to show selected value as summary
|
||||
ListPreference licensePreference = (ListPreference) findPreference(Prefs.DEFAULT_LICENSE);
|
||||
licensePreference.setSummary(getString(Utils.licenseNameFor(licensePreference.getValue())));
|
||||
|
||||
// Keep summary updated when changing value
|
||||
licensePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
|
|
@ -38,5 +41,44 @@ public class SettingsFragment extends PreferenceFragment {
|
|||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads");
|
||||
SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(getActivity().getApplicationContext());
|
||||
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
|
||||
uploadLimit.setSummary(uploads + "");
|
||||
uploadLimit.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
int value = Integer.parseInt(newValue.toString());
|
||||
final SharedPreferences sharedPref = PreferenceManager
|
||||
.getDefaultSharedPreferences(getActivity().getApplicationContext());
|
||||
final SharedPreferences.Editor editor = sharedPref.edit();
|
||||
if (value > 500) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.maximum_limit)
|
||||
.setMessage(R.string.maximum_limit_alert)
|
||||
.setPositiveButton(android.R.string.yes,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
}
|
||||
})
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.show();
|
||||
editor.putInt(Prefs.UPLOADS_SHOWING, 500);
|
||||
uploadLimit.setSummary(500 + "");
|
||||
uploadLimit.setText(500 + "");
|
||||
} else {
|
||||
editor.putInt(Prefs.UPLOADS_SHOWING, Integer.parseInt(newValue.toString()));
|
||||
uploadLimit.setSummary(newValue.toString());
|
||||
}
|
||||
editor.apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package fr.free.nrw.commons.ui.widget;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
public abstract class OverlayDialog extends DialogFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(STYLE_NORMAL, R.style.borderless_dialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
setDialogLayoutToFullScreen();
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
private void setDialogLayoutToFullScreen() {
|
||||
Window window = getDialog().getWindow();
|
||||
WindowManager.LayoutParams wlp = window.getAttributes();
|
||||
window.requestFeature(Window.FEATURE_NO_TITLE);
|
||||
wlp.gravity = Gravity.BOTTOM;
|
||||
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
wlp.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
window.setAttributes(wlp);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
Window window = dialog.getWindow();
|
||||
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
return dialog;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,8 @@ import android.content.Intent;
|
|||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import fr.free.nrw.commons.MWApi;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class ShareActivity
|
|||
Toast startingToast = Toast.makeText(getApplicationContext(), R.string.uploading_started, Toast.LENGTH_LONG);
|
||||
startingToast.show();
|
||||
|
||||
if (cacheFound == false) {
|
||||
if (!cacheFound) {
|
||||
//Has to be called after apiCall.request()
|
||||
app.cacheData.cacheCategory();
|
||||
Timber.d("Cache the categories found");
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import android.support.v4.app.NotificationCompat;
|
|||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.Toast;
|
||||
|
||||
import fr.free.nrw.commons.*;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
|
|
@ -25,11 +25,6 @@ import java.util.Set;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.EventLog;
|
||||
import fr.free.nrw.commons.HandlerService;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||
|
|
|
|||
80
app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class DialogUtil {
|
||||
|
||||
public static void dismissSafely(@Nullable Activity activity, @Nullable DialogFragment dialog) {
|
||||
boolean isActivityDestroyed = false;
|
||||
|
||||
if (activity == null || dialog == null) {
|
||||
Timber.d("dismiss called with null activity / dialog. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
isActivityDestroyed = activity.isDestroyed();
|
||||
}
|
||||
if (activity.isFinishing() || isActivityDestroyed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dialog.dismiss();
|
||||
|
||||
} catch (IllegalStateException e) {
|
||||
Timber.e(e, "Could not dismiss dialog.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void showSafely(Activity activity, Dialog dialog) {
|
||||
if (activity == null || dialog == null) {
|
||||
Timber.d("Show called with null activity / dialog. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isActivityDestroyed = false;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
isActivityDestroyed = activity.isDestroyed();
|
||||
}
|
||||
if (activity.isFinishing() || isActivityDestroyed) {
|
||||
Timber.e("Activity is not running. Could not show dialog. ");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
dialog.show();
|
||||
} catch (IllegalStateException e) {
|
||||
Timber.e(e, "Could not show dialog.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void showSafely(FragmentActivity activity, DialogFragment dialog) {
|
||||
boolean isActivityDestroyed = false;
|
||||
|
||||
if (activity == null || dialog == null) {
|
||||
Timber.d("show called with null activity / dialog. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
isActivityDestroyed = activity.isDestroyed();
|
||||
}
|
||||
if (activity.isFinishing() || isActivityDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (dialog.getDialog() == null || !dialog.getDialog().isShowing()) {
|
||||
dialog.show(activity.getSupportFragmentManager(), dialog.getClass().getSimpleName());
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
Timber.e(e, "Could not show dialog.");
|
||||
}
|
||||
}
|
||||
}
|
||||
33
app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class FileUtils {
|
||||
public static String readFromFile(Context context, String fileName) {
|
||||
String stringBuilder = "";
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(
|
||||
new InputStreamReader(context.getAssets().open(fileName), "UTF-8"));
|
||||
String mLine;
|
||||
while ((mLine = reader.readLine()) != null) {
|
||||
stringBuilder += mLine + "\n";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
//log the exception
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
//log the exception
|
||||
}
|
||||
}
|
||||
}
|
||||
return stringBuilder;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class UriDeserializer implements JsonDeserializer<Uri> {
|
||||
@Override
|
||||
public Uri deserialize(final JsonElement src, final Type srcType,
|
||||
final JsonDeserializationContext context) throws JsonParseException {
|
||||
return Uri.parse(src.getAsString());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
|
||||
public class UriSerializer implements JsonSerializer<Uri> {
|
||||
public JsonElement serialize(Uri src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return new JsonPrimitive(src.toString());
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_map_black_24dp.png
Normal file
|
After Width: | Height: | Size: 310 B |
BIN
app/src/main/res/drawable-hdpi/ic_map_white_24dp.png
Normal file
|
After Width: | Height: | Size: 317 B |
BIN
app/src/main/res/drawable-mdpi/ic_map_black_24dp.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
app/src/main/res/drawable-mdpi/ic_map_white_24dp.png
Normal file
|
After Width: | Height: | Size: 231 B |
BIN
app/src/main/res/drawable-xhdpi/ic_map_black_24dp.png
Normal file
|
After Width: | Height: | Size: 363 B |
BIN
app/src/main/res/drawable-xhdpi/ic_map_white_24dp.png
Normal file
|
After Width: | Height: | Size: 373 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_map_black_24dp.png
Normal file
|
After Width: | Height: | Size: 513 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_map_white_24dp.png
Normal file
|
After Width: | Height: | Size: 528 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_map_black_24dp.png
Normal file
|
After Width: | Height: | Size: 667 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_map_white_24dp.png
Normal file
|
After Width: | Height: | Size: 678 B |
|
|
@ -5,6 +5,13 @@
|
|||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
97
app/src/main/res/layout/dialog_nearby_info.xml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
app:behavior_peekHeight="@dimen/bottom_peak_height">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:minHeight="@dimen/bottom_peak_height"
|
||||
android:background="@android:color/white"
|
||||
android:animateLayoutChanges="true">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/link_preview_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:minHeight="64dp">
|
||||
<TextView
|
||||
android:id="@+id/link_preview_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:paddingBottom="4dp"
|
||||
android:textSize="20sp"
|
||||
android:fontFamily="serif"
|
||||
android:lineSpacingMultiplier="0.9"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:textColor="@android:color/black"
|
||||
tools:text="Lorem ipsum" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:background="@android:color/black"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_preview_extract"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:textSize="16sp"
|
||||
android:lineSpacingMultiplier="1.3"
|
||||
android:textIsSelectable="true"
|
||||
android:textColor="@android:color/black"
|
||||
tools:text="Lorem ipsum"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/link_preview_bottom_padding"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="70dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/link_preview_directions_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:textColor="@android:color/black"
|
||||
android:text="GET DIRECTIONS"/>
|
||||
<TextView
|
||||
android:id="@+id/link_preview_go_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:textColor="@android:color/black"
|
||||
android:text="READ ARTICLE"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
|
@ -159,6 +159,35 @@
|
|||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/subBackground"
|
||||
android:padding="16dp"
|
||||
>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:text="Coordinates"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:paddingBottom="6dp"
|
||||
/>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="License link"
|
||||
android:id="@+id/mediaDetailCoordinates"
|
||||
android:layout_gravity="left|start"
|
||||
android:background="?attr/subBackground"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="14sp"
|
||||
android:padding="12dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
|
||||
<fr.free.nrw.commons.media.MediaDetailSpacer
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
|
|
|
|||
|
|
@ -4,13 +4,6 @@
|
|||
android:orientation="vertical"
|
||||
>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
/>
|
||||
|
||||
<ListView
|
||||
android:id="@+id/listView"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_map"
|
||||
android:title="@string/refresh_button"
|
||||
android:icon="@drawable/ic_map_white_24dp"
|
||||
android:orderInCategory="1"
|
||||
app:showAsAction="ifRoom"
|
||||
/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_refresh"
|
||||
android:title="@string/refresh_button"
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ Dodirnite ovu poruku (ili pritisnite dugme \"nazad\") da biste preskočili ovaj
|
|||
<string name="title_activity_settings">Postavke</string>
|
||||
<string name="title_activity_signup">Registracija</string>
|
||||
<string name="menu_about">O</string>
|
||||
<string name="about_license" fuzzy="true">Program otvorenog kod objavljen pod licencom <a href=\"https://github.com/wikimedia/apps-android-commons/blob/master/COPYING\">Apache v2</a>. Wikimedia Commons i njen logo zaštitni su znaci Zadužbine Wikimedia i koriste se s njenom dozvolom. Nismo povezani niti nas podržava Zadužbina Wikimedia.</string>
|
||||
<string name="about_license">Program otvorenog kod objavljen pod licencom <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache v2</a>. Wikimedia Commons i njen logo zaštitni su znaci Zadužbine Wikimedia i koriste se s njenom dozvolom. Nismo povezani niti nas podržava Zadužbina Wikimedia.</string>
|
||||
<string name="about_improve"><a href=\"https://github.com/commons-app/apps-android-commons\">Izvorni kod</a> i <a href=\"https://commons-app.github.io/\">veb-sajt</a> na GitHubu. Započnite novi <a href=\"https://github.com/commons-app/apps-android-commons/issues\">slučaj na GitHubu</a> da biste prijavili greške i dali prijedloge.</string>
|
||||
<string name="about_privacy_policy"><a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Politika privatnosti</a></string>
|
||||
<string name="title_activity_about">O</string>
|
||||
|
|
|
|||
56
app/src/main/res/values-ckb/strings.xml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">کۆمنز</string>
|
||||
<string name="menu_settings">ڕێکخستنەکان</string>
|
||||
<string name="username">ناوی بەکارھێنەر</string>
|
||||
<string name="password">تێپەڕوشە</string>
|
||||
<string name="login">چوونە ژوورەوە</string>
|
||||
<string name="signup">خۆ تۆمارکردن</string>
|
||||
<string name="logging_in_title">چوونە ژوورەوە...</string>
|
||||
<string name="logging_in_message">تکایە چاوەڕوان بە...</string>
|
||||
<string name="login_success">بە سەرکەوتووانە چوویتە ژوورەوە!</string>
|
||||
<string name="login_failed">چوونە ژوورەوە شکستی ھێنا!</string>
|
||||
<string name="upload_failed">پەڕگە نەدۆزرایەوە. تکایە پەڕگەیەکی تر تاقی بکەرەوە.</string>
|
||||
<string name="uploading_started">بارکردن دەستی پێکرد!</string>
|
||||
<string name="upload_completed_notification_title">%1$ بارکرا!</string>
|
||||
<string name="upload_completed_notification_text">کرتە بکە بۆ بینینی بارەکەت</string>
|
||||
<string name="upload_progress_notification_title_start">دەستپێکردنی بارکردنی %1</string>
|
||||
<string name="upload_progress_notification_title_in_progress">%1$ بار دەکرێت</string>
|
||||
<string name="upload_progress_notification_title_finishing">تەواو بوونی بارکردنی %1$</string>
|
||||
<string name="upload_failed_notification_title">بارکردنی %1 شکستی ھێنا</string>
|
||||
<string name="upload_failed_notification_subtitle">کرتە بکە بۆ بینین</string>
|
||||
<string name="title_activity_about">سەبارەت</string>
|
||||
<string name="no_uploads_yet">تاکو ئێستا ھیچ پەڕگەیەکت بار نەکردووە.</string>
|
||||
<string name="menu_retry_upload">ھەوڵداناوە</string>
|
||||
<string name="menu_cancel_upload">ھەڵوەشاندنەوە</string>
|
||||
<string name="share_license_summary">ئەم پەڕەیە مۆڵەتنامەی %1 پێ دەدرێت</string>
|
||||
<string name="menu_download">داگرتن</string>
|
||||
<string name="preference_license">مۆڵەتنامە</string>
|
||||
<string name="preference_theme">مۆدی شەو</string>
|
||||
<string name="license_name_fal">مۆڵەتنامەی ھونەری ئازاد</string>
|
||||
<string name="license_name_pd_old_100">پاوانی گشتی (دروستکەر ١٠٠ ساڵ لەمەوبەر مردووە)</string>
|
||||
<string name="license_name_pd_old">پاوانی گشتی (مافی لەبەرگرتنەوە بەسەرچووە)</string>
|
||||
<string name="license_name_pd_art">پاوانی گشتی (ھونەر)</string>
|
||||
<string name="license_name_pd_us">پاوانی گشتی (ویلایەتە یەکگرتووەکان)</string>
|
||||
<string name="welcome_wikipedia_subtext">وێنەکانی سەر ویکیپیدیا لە ویکیمیدیا کۆمنزەوە دەھێندرێن.</string>
|
||||
<string name="welcome_copyright_text">وێنەکانت یارمەتی فێرکردنی خەڵکی دەدات لە سەرانسەری جیھان دا.</string>
|
||||
<string name="welcome_copyright_subtext">خۆت لەو کارانە لابدە کە مافی بڵاوکردنەوەیان پارێزراوە وەک ئەو شتانەی کە لەسەر ئینتەرنێت دەیان دۆزیتەوە، بە تایبەتی وێنەی پۆستەر، بەرگی کتێب و یاری، ھتد...</string>
|
||||
<string name="welcome_final_text">پێت وایە تێگەیشتوویت؟</string>
|
||||
<string name="welcome_final_button_text">بەڵێ!</string>
|
||||
<string name="detail_panel_cats_label">پۆلەكان</string>
|
||||
<string name="detail_panel_cats_loading">باركردن...</string>
|
||||
<string name="detail_panel_cats_none">ھیچ ھەڵنەبژێردراوە</string>
|
||||
<string name="detail_description_empty">بێ وەسف...</string>
|
||||
<string name="detail_license_empty">مۆڵەتنامە نەزانراوە</string>
|
||||
<string name="menu_refresh">نوێکردنەوە</string>
|
||||
<string name="ok">باشە</string>
|
||||
<string name="title_activity_nearby">شوێنە نزیکەکان</string>
|
||||
<string name="warning">ئاگاداری</string>
|
||||
<string name="file_exists">ئەم پەڕگەیە لەسەر کۆمنز ھەیە. دڵنیایت کە دەتەوێت بەردەوام بیت؟</string>
|
||||
<string name="yes">بەڵێ</string>
|
||||
<string name="no">نەخێر</string>
|
||||
<string name="media_detail_title">ناونیشان</string>
|
||||
<string name="media_detail_media_title">ناونیشانی میدیا</string>
|
||||
<string name="media_detail_description">وەسف</string>
|
||||
<string name="use_wikidata">بەکارھێنانی ویکیدراوە</string>
|
||||
</resources>
|
||||
|
|
@ -162,4 +162,5 @@ Napauta tätä viestiä (tai paina takaisin) ohittaaksesi tämän vaiheen.</stri
|
|||
<string name="media_detail_title">Otsikko</string>
|
||||
<string name="media_detail_description">Kuvaus</string>
|
||||
<string name="become_a_tester_title">Ryhdy beetatestaajaksi</string>
|
||||
<string name="use_wikidata">Käytä Wikidataa</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@
|
|||
<string name="use_previous">पिछले शीर्षक/विवरण का उपयोग करें</string>
|
||||
<string name="allow_gps">वर्तमान स्थान स्वतः ज्ञात करें</string>
|
||||
<string name="allow_gps_summary">यदि छवि जियोटैगेड नहीं है तो श्रेणियों के सुझाव हेतु वर्तमान स्थान ज्ञात करें।</string>
|
||||
<string name="preference_theme">रात्रि मोड</string>
|
||||
<string name="preference_theme_summary">डार्क थीम का प्रयोग करें</string>
|
||||
<string name="license_name_cc_by_sa_four">विशेषता-साझेदारी 4.0</string>
|
||||
<string name="license_name_cc_by_four">एट्रिब्यूशन 4.0</string>
|
||||
<string name="license_name_cc_by_sa">एट्रीबुसन-शेयरअलाइक 3.0</string>
|
||||
<string name="license_name_cc_by">एट्रीबुसन 3.0</string>
|
||||
|
|
@ -165,4 +168,5 @@
|
|||
<string name="media_detail_media_title">मीडिया का शीर्षक</string>
|
||||
<string name="media_detail_description">विवरण</string>
|
||||
<string name="become_a_tester_title">बीटा परीक्षक बनें</string>
|
||||
<string name="use_wikidata">विकिडेटा का प्रयोग करें</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Trykk på denne meldingen (eller trykk tilbake) for å hoppe over dette steget.<
|
|||
<string name="title_activity_settings">Innstillinger</string>
|
||||
<string name="title_activity_signup">Registrer deg</string>
|
||||
<string name="menu_about">Om</string>
|
||||
<string name="about_license" fuzzy="true">Programvare med åpen kildekode sluppet under <a href=\"https://github.com/wikimedia/apps-android-commons/blob/master/COPYING\">Apache License v2</a>. Wikimedia Commons og dets logo er varemerker fra Wikimedia Foundation og brukes med tillatelse fra dem. Vi er verken støttet av eller koblet til Wikimedia Foundation.</string>
|
||||
<string name="about_license">Programvare med åpen kildekode sluppet under <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a>. Wikimedia Commons og dets logo er varemerker fra Wikimedia Foundation og brukes med tillatelse fra dem. Vi er verken støttet av eller koblet til Wikimedia Foundation.</string>
|
||||
<string name="about_improve"><a href=\"https://github.com/commons-app/apps-android-commons\">Kildekode</a> og <a href=\"https://commons-app.github.io/\">nettside</a> på GitHub. Opprett en ny <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-sak</a> for feilrapporter og forslag.</string>
|
||||
<string name="about_privacy_policy"><a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Personvernpolicy</a></string>
|
||||
<string name="about_credits"><a href=\"https://github.com/commons-app/apps-android-comons/blob/master/CREDITS\">CREDITS</a></string>
|
||||
|
|
@ -170,4 +170,6 @@ Trykk på denne meldingen (eller trykk tilbake) for å hoppe over dette steget.<
|
|||
<string name="media_detail_description_explanation">Beskrivelse av mediet skal være her. Denne kan potensielt være ganske lang, og vil trenge å strekke seg over flere linjer. Vi håper det ser bra nok ut.</string>
|
||||
<string name="become_a_tester_title">Bli betatester</string>
|
||||
<string name="become_a_tester_description">Registrer deg på vår betakanal på Google Play og få tidlig tilgang til nye funksjoner og feilrettinger</string>
|
||||
<string name="use_wikidata">Bruk Wikidata</string>
|
||||
<string name="use_wikidata_summary">(Advarsel: Store mengder mobildata kan bli brukt om denne slås av)</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<string name="logging_in_message">Сачекајте…</string>
|
||||
<string name="login_success">Успешно сте пријављени.</string>
|
||||
<string name="login_failed">Пријављивање није успело.</string>
|
||||
<string name="upload_failed">Датотека није пронађена. Покушајте са другом датотеком.</string>
|
||||
<string name="authentication_failed">Провера идентитета није успела.</string>
|
||||
<string name="uploading_started">Отпремање је започето.</string>
|
||||
<string name="upload_completed_notification_title">Датотека „%1$s“ је отпремљена.</string>
|
||||
|
|
@ -74,7 +75,7 @@
|
|||
<string name="title_activity_signup">Отвори налог</string>
|
||||
<string name="menu_about">О апликацији</string>
|
||||
<string name="about_license">Софтвер отвореног кода доступан под лиценцом <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache вер. 2</a> Викимедијина Остава и њен лого су заштитни знаци Викимедијине Фондације и користе се са дозволом Викимедијине Фондацине. Ми не одобравамо или подржавмо Викимедијину Фондацију.</string>
|
||||
<string name="about_improve" fuzzy="true">Изворни кôд се налази на <a href=\"https://github.com/wikimedia/apps-android-commons\">GitHub-у</a>. Програмске грешке су на сајту <a href=\"https://bugzilla.wikimedia.org/enter_bug.cgi?product=Commons%20App\">Bugzilla</a>.</string>
|
||||
<string name="about_improve"><a href=\"https://github.com/commons-app/apps-android-commons\">Изворни кôд</a> и <a href=\"https://commons-app.github.io/\">веб-сајт</a> на GitHub-у. Направите нови <a href=\"https://github.com/commons-app/apps-android-commons/issues\">захтев на GitHub-у</a> да бисте пријавили грешке или дали предлоге.</string>
|
||||
<string name="about_privacy_policy"><a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Политика приватности</a></string>
|
||||
<string name="about_credits"><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">ЗАСЛУГЕ</a></string>
|
||||
<string name="title_activity_about">О апликацији</string>
|
||||
|
|
@ -89,7 +90,7 @@
|
|||
<string name="menu_download">Преузми</string>
|
||||
<string name="preference_license">Лиценца</string>
|
||||
<string name="use_previous">Користи претходан наслов/опис</string>
|
||||
<string name="allow_gps">Аутоматски дохвати тренутну локацију</string>
|
||||
<string name="allow_gps">Аутоматски детектуј тренутну локацију</string>
|
||||
<string name="allow_gps_summary">Прими тренутну локацију да би предложили категорију ако слика није географски означена</string>
|
||||
<string name="preference_theme">Ноћни режим</string>
|
||||
<string name="preference_theme_summary">Користити тамну тему</string>
|
||||
|
|
@ -140,6 +141,9 @@
|
|||
- Слике које сте преузели са интернета
|
||||
- Скриншотове из сопствених апликација</string>
|
||||
<string name="tutorial_4_text">Пример отпремања:</string>
|
||||
<string name="tutorial_4_subtext">— Наслов: Сиднејска опера
|
||||
— Опис: Сиднејска опера, поглед преко залива
|
||||
— Категорије: Сиднејска опера, Сиднејска опера са запада, погледи на Сиднејксу оперу из даљине</string>
|
||||
<string name="welcome_wikipedia_text">Делите своје слике. Оживите чланке на Википедији!</string>
|
||||
<string name="welcome_wikipedia_subtext">Слике на Википедији долазе из Оставе.</string>
|
||||
<string name="welcome_copyright_text">Са вашим сликама помажете у образовању људи широм света.</string>
|
||||
|
|
@ -163,7 +167,9 @@
|
|||
<string name="media_detail_title">Наслов</string>
|
||||
<string name="media_detail_media_title">Наслов медија</string>
|
||||
<string name="media_detail_description">Опис</string>
|
||||
<string name="media_detail_description_explanation">Опис датотеке иде овде. Може да буде поприлично дуг и приказиваће се у више редова. Надамо се да ће изгледати лепо.</string>
|
||||
<string name="become_a_tester_title">Постани Бета Тестер</string>
|
||||
<string name="become_a_tester_description">Прикључите се на наш бета канал на Гугл плеју и приступајте новим информацијама и поправкама багова</string>
|
||||
<string name="use_wikidata">Користи Википодатке</string>
|
||||
<string name="use_wikidata_summary">(Упозорење: онемогућавањем овога може се изазвати велика потрошња мобилних података)</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@
|
|||
<dimen name="icon_size">120dp</dimen>
|
||||
<dimen name="tiny_margin">4dp</dimen>
|
||||
<dimen name="small_margin">8dp</dimen>
|
||||
<dimen name="bottom_peak_height">240dp</dimen>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@
|
|||
<string name="upload_failed_notification_title">Uploading %1$s failed</string>
|
||||
<string name="upload_failed_notification_subtitle">Tap to view</string>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">1 file uploading</item>
|
||||
<item quantity="one">%d file uploading</item>
|
||||
<item quantity="other">%d files uploading</item>
|
||||
</plurals>
|
||||
<string name="title_activity_contributions">My uploads</string>
|
||||
<string name="title_activity_contributions">My Recent Uploads</string>
|
||||
<string name="contribution_state_queued">Queued</string>
|
||||
<string name="contribution_state_failed">Failed</string>
|
||||
<string name="contribution_state_in_progress">%1$d%% complete</string>
|
||||
|
|
@ -42,6 +42,7 @@
|
|||
<string name="login_failed_password">Unable to login - please check your password</string>
|
||||
<string name="login_failed_throttled">Too many unsuccessful attempts. Please try again in a few minutes.</string>
|
||||
<string name="login_failed_blocked">Sorry, this user has been blocked on Commons</string>
|
||||
<string name="login_failed_2fa_not_supported">The app doesn\'t currently support 2 Factor Authentication.</string>
|
||||
<string name="login_failed_generic">Login failed</string>
|
||||
<string name="share_upload_button">Upload</string>
|
||||
<string name="multiple_share_base_title">Name this set</string>
|
||||
|
|
@ -50,17 +51,19 @@
|
|||
<string name="categories_search_text_hint">Search categories</string>
|
||||
<string name="menu_save_categories">Save</string>
|
||||
<string name="refresh_button">Refresh</string>
|
||||
<string name="contributions_subtitle_zero">No uploads yet</string>
|
||||
<plurals name="contributions_subtitle">
|
||||
<item quantity="zero">No uploads yet</item>
|
||||
<item quantity="one">1 upload</item>
|
||||
<!--zero is not used in english. Category mentioned here for easy reference in future-->
|
||||
<item quantity="zero">@string/contributions_subtitle_zero</item>
|
||||
<item quantity="one">%d upload</item>
|
||||
<item quantity="other">%d uploads</item>
|
||||
</plurals>
|
||||
<plurals name="starting_multiple_uploads">
|
||||
<item quantity="one">Starting 1 upload</item>
|
||||
<item quantity="one">Starting %d upload</item>
|
||||
<item quantity="other">Starting %d uploads</item>
|
||||
</plurals>
|
||||
<plurals name="multiple_uploads_title">
|
||||
<item quantity="one">1 upload</item>
|
||||
<item quantity="one">%d upload</item>
|
||||
<item quantity="other">%d uploads</item>
|
||||
</plurals>
|
||||
<string name="categories_not_found">No categories matching %1$s found</string>
|
||||
|
|
@ -172,4 +175,12 @@ Tap this message (or hit back) to skip this step.</string>
|
|||
<string name="beta_opt_in_link">https://play.google.com/apps/testing/fr.free.nrw.commons</string>
|
||||
<string name="use_wikidata">Use Wikidata</string>
|
||||
<string name="use_wikidata_summary">(Warning: disabling this may cause large mobile data consumption)</string>
|
||||
<string name="map_theme_light">mapbox://styles/mapbox/traffic-day-v2</string>
|
||||
<string name="map_theme_dark">mapbox://styles/mapbox/traffic-night-v2</string>
|
||||
<string name="mapbox_commons_app_token">pk.eyJ1IjoibWFza2FyYXZpdmVrIiwiYSI6ImNqMmxvdzFjMTAwMHYzM283ZWM3eW5tcDAifQ.ib5SZ9EVjwJe6GSKve0bcg</string>
|
||||
<string name="number_of_uploads">My Recent Upload Limit</string>
|
||||
<string name="maximum_limit">Maximum Limit</string>
|
||||
<string name="maximum_limit_alert">Maximum limit should be 500</string>
|
||||
<string name="set_limit">Set Recent Upload Limit</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -45,4 +45,10 @@
|
|||
|
||||
<style name="ProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal" />
|
||||
|
||||
<style name="borderless_dialog">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
|
@ -38,4 +38,12 @@
|
|||
android:summary="@string/use_wikidata_summary"
|
||||
/>
|
||||
|
||||
<EditTextPreference
|
||||
android:key="uploads"
|
||||
android:defaultValue="100"
|
||||
android:title= "@string/set_limit"
|
||||
android:inputType="numberDecimal"
|
||||
android:maxLength="3"
|
||||
/>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UtilsFixExtensionTest {
|
||||
|
||||
@Test public void jpegResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpeg", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void capitalJpegWithNoHintResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.JPEG", null), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpegWithBogusHintResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpeg", null), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpegToCapitalJpegResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpeg", "JPEG"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpgToJpegResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpg", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpegToJpgResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpeg", "jpg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpgRemainsJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.jpg", "jpg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void pngRemainsPng() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile.png", "png"), is("SampleFile.png"));
|
||||
}
|
||||
|
||||
@Test public void jpgHintResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile", "jpg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void jpegHintResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SampleFile", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void dotLessJpgToJpgResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("SAMPLEjpg", "jpg"), is("SAMPLEjpg.jpg"));
|
||||
}
|
||||
|
||||
@Test public void inWordJpegToJpgResultsInJpg() {
|
||||
Assert.assertThat(Utils.fixExtension("X.jpeg.SAMPLE", "jpg"),is("X.jpeg.SAMPLE.jpg"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +1,16 @@
|
|||
package fr.free.nrw.commons;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UtilsTest {
|
||||
|
||||
@Test public void fixExtensionJpegToJpeg() {
|
||||
assertThat(Utils.fixExtension("SampleFile.jpeg", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionJpegToJpg() {
|
||||
assertThat(Utils.fixExtension("SampleFile.JPEG", null), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionNull() {
|
||||
assertThat(Utils.fixExtension("SampleFile.jpeg", "JPEG"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionJpgToJpeg() {
|
||||
assertThat(Utils.fixExtension("SampleFile.jpg", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionJpgToJpg() {
|
||||
assertThat(Utils.fixExtension("SampleFile.jpg", "jpg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionPngToPng() {
|
||||
assertThat(Utils.fixExtension("SampleFile.png", "png"), is("SampleFile.png"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionEmptyToJpg() {
|
||||
assertThat(Utils.fixExtension("SampleFile", "jpg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionEmptyToJpeg() {
|
||||
assertThat(Utils.fixExtension("SampleFile", "jpeg"), is("SampleFile.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionJpgNotExtension() {
|
||||
assertThat(Utils.fixExtension("SAMPLEjpg", "jpg"), is("SAMPLEjpg.jpg"));
|
||||
}
|
||||
|
||||
@Test public void fixExtensionJpegNotExtension() {
|
||||
assertThat(Utils.fixExtension("SAMPLE.jpeg.SAMPLE", "jpg"), is("SAMPLE.jpeg.SAMPLE.jpg"));
|
||||
}
|
||||
|
||||
@Test public void stripLocalizedStringPass() {
|
||||
assertThat(Utils.stripLocalizedString("Hello"), is("Hello"));
|
||||
Assert.assertThat(Utils.stripLocalizedString("Hello"), is("Hello"));
|
||||
}
|
||||
|
||||
@Test public void stripLocalizedStringJa() {
|
||||
assertThat(Utils.stripLocalizedString("\"こんにちは\"@ja"), is("こんにちは"));
|
||||
Assert.assertThat(Utils.stripLocalizedString("\"こんにちは\"@ja"), is("こんにちは"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@ android.useDeprecatedNdk=true
|
|||
|
||||
# Library dependencies
|
||||
BUTTERKNIFE_VERSION=8.4.0
|
||||
|
||||
|
|
|
|||