Synced branch to master

This commit is contained in:
Vishan Seru 2017-10-10 19:51:29 +05:30
commit 6b96b65203
123 changed files with 1667 additions and 930 deletions

View file

@ -12,20 +12,24 @@ dependencies {
compile 'ch.acra:acra:4.7.0'
compile 'org.mediawiki:api:1.3'
compile 'commons-codec:commons-codec:1.10'
compile "com.android.support:support-v4:${project.supportLibVersion}"
compile "com.android.support:appcompat-v7:${project.supportLibVersion}"
compile "com.android.support:design:${project.supportLibVersion}"
compile 'com.google.code.gson:gson:2.8.0'
compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
compile 'com.github.pedrovgs:renderers:3.3.3'
annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.jakewharton.timber:timber:4.5.1'
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okio:okio:1.13.0'
compile 'info.debatty:java-string-similarity:0.24'
compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.1.0@aar'){
transitive=true
}
compile "com.android.support:support-v4:${project.supportLibVersion}"
compile "com.android.support:appcompat-v7:${project.supportLibVersion}"
compile "com.android.support:design:${project.supportLibVersion}"
compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okio:okio:1.13.0'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
@ -77,6 +81,33 @@ android {
}
}
productFlavors {
prod {
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.wikimedia.org/wikipedia/commons\""
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\""
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\""
buildConfigField "String", "EVENTLOG_URL", "\"https://www.wikimedia.org/beacon/event\""
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
}
beta {
// What values do we need to hit the BETA versions of the site / api ?
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\""
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.beta.wmflabs.org/wikipedia/commons\""
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\""
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\""
buildConfigField "String", "EVENTLOG_URL", "\"https://commons.wikimedia.beta.wmflabs.org/beacon/event\""
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
}
}
lintOptions {
disable 'MissingTranslation'
disable 'ExtraTranslation'

View file

@ -1,23 +0,0 @@
package fr.free.nrw.commons;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.is;
// TODO: use Robolectric and make it runnable without a connected device
@RunWith(AndroidJUnit4.class)
public class MediaTest {
@Test public void displayTitleShouldStripExtension() {
Media m = new Media("File:Example.jpg");
Assert.assertThat(m.getDisplayTitle(), is("Example"));
}
@Test public void displayTitleShouldUseSpaceForUnderscore() {
Media m = new Media("File:Example 1_2.jpg");
Assert.assertThat(m.getDisplayTitle(), is("Example 1 2"));
}
}

View file

@ -1,57 +0,0 @@
package fr.free.nrw.commons;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.List;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.Place;
import static org.hamcrest.CoreMatchers.is;
@RunWith(AndroidJUnit4.class)
public class NearbyControllerTest {
private Context instrumentationContext;
@Before
public void setup() {
instrumentationContext = InstrumentationRegistry.getTargetContext();
}
@Test public void testNullAttractions() {
LatLng location = new LatLng(0, 0, 0);
List<NearbyBaseMarker> options =
NearbyController.loadAttractionsFromLocationToBaseMarkerOptions(
location,
null,
instrumentationContext
);
Assert.assertThat(options.size(), is(0));
}
@Test public void testEmptyList() {
LatLng location = new LatLng(0, 0, 0);
List<Place> emptyList = new ArrayList<>();
List<NearbyBaseMarker> options =
NearbyController.loadAttractionsFromLocationToBaseMarkerOptions(
location,
emptyList,
instrumentationContext
);
Assert.assertThat(options.size(), is(0));
}
}

View file

@ -1,61 +0,0 @@
package fr.free.nrw.commons;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.URLEncoder;
import static org.hamcrest.CoreMatchers.is;
// TODO: use Robolectric and make it runnable without a connected device
@RunWith(AndroidJUnit4.class)
public class PageTitleTest {
@Test public void displayTextShouldNotBeUnderscored() {
Assert.assertThat(new PageTitle("Ex_1 ").getDisplayText(),
is("Ex 1"));
}
@Test public void moreThanTwoColons() {
Assert.assertThat(new PageTitle("File:sample:a.jpg").getPrefixedText(),
is("File:Sample:a.jpg"));
}
@Test public void getTextShouldReturnWithoutNamespace() {
Assert.assertThat(new PageTitle("File:sample.jpg").getText(),
is("Sample.jpg"));
}
@Test public void capitalizeNameAfterNamespace() {
Assert.assertThat(new PageTitle("File:sample.jpg").getPrefixedText(),
is("File:Sample.jpg"));
}
@Test public void prefixedTextShouldBeUnderscored() {
Assert.assertThat(new PageTitle("Ex 1 ").getPrefixedText(),
is("Ex_1"));
}
@Test public void getMobileUriForTest() {
Assert.assertThat(new PageTitle("Test").getMobileUri().toString(),
is("https://commons.m.wikimedia.org/wiki/Test"));
}
@Test public void spaceBecomesUnderscoreInUri() {
Assert.assertThat(new PageTitle("File:Ex 1.jpg").getCanonicalUri().toString(),
is("https://commons.wikimedia.org/wiki/File:Ex_1.jpg"));
}
@Test public void leaveSubpageNamesUncapitalizedInUri() {
Assert.assertThat(new PageTitle("User:Ex/subpage").getCanonicalUri().toString(),
is("https://commons.wikimedia.org/wiki/User:Ex/subpage"));
}
@Test public void unicodeUri() throws Throwable {
Assert.assertThat(new PageTitle("User:例").getCanonicalUri().toString(),
is("https://commons.wikimedia.org/wiki/User:" + URLEncoder.encode("", "utf-8")));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -13,7 +13,6 @@ import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.preference.PreferenceManager;
import android.support.v4.util.LruCache;
import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.stetho.Stetho;
@ -35,7 +34,6 @@ import fr.free.nrw.commons.modifications.ModifierSequence;
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.nearby.NearbyPlaces;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.FileUtils;
import timber.log.Timber;
@ -51,12 +49,6 @@ import timber.log.Timber;
public class CommonsApplication extends Application {
private Account currentAccount = null; // Unlike a savings account...
public static final String API_URL = "https://commons.wikimedia.org/w/api.php";
public static final String IMAGE_URL_BASE = "https://upload.wikimedia.org/wikipedia/commons";
public static final String HOME_URL = "https://commons.wikimedia.org/wiki/";
public static final String MOBILE_HOME_URL = "https://commons.m.wikimedia.org/wiki/";
public static final String EVENTLOG_URL = "https://www.wikimedia.org/beacon/event";
public static final String EVENTLOG_WIKI = "commonswiki";
public static final Object[] EVENT_UPLOAD_ATTEMPT = {"MobileAppUploadAttempts", 5334329L};
public static final Object[] EVENT_LOGIN_ATTEMPT = {"MobileAppLoginAttempts", 5257721L};
@ -92,7 +84,7 @@ public class CommonsApplication extends Application {
public MediaWikiApi getMWApi() {
if (api == null) {
api = new ApacheHttpClientMediaWikiApi(API_URL);
api = new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
}
return api;
}
@ -155,10 +147,10 @@ public class CommonsApplication extends Application {
* @return Account|null
*/
public Account getCurrentAccount() {
if(currentAccount == null) {
if (currentAccount == null) {
AccountManager accountManager = AccountManager.get(this);
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.accountType());
if(allAccounts.length != 0) {
if (allAccounts.length != 0) {
currentAccount = allAccounts[0];
}
}
@ -169,7 +161,7 @@ public class CommonsApplication extends Application {
AccountManager accountManager = AccountManager.get(this);
Account curAccount = getCurrentAccount();
if(curAccount == null) {
if (curAccount == null) {
return false; // This should never happen
}
@ -190,7 +182,7 @@ public class CommonsApplication extends Application {
pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}
public void clearApplicationData(Context context, NavigationBaseActivity.LogoutListener logoutListener) {
public void clearApplicationData(Context context, LogoutListener logoutListener) {
File cacheDirectory = context.getCacheDir();
File applicationDirectory = new File(cacheDirectory.getParent());
if (applicationDirectory.exists()) {
@ -222,10 +214,8 @@ public class CommonsApplication extends Application {
setIndex(getIndex() + 1);
try {
if (accountManagerFuture != null) {
if (accountManagerFuture.getResult()) {
Timber.d("Account removed successfully.");
}
if (accountManagerFuture != null && accountManagerFuture.getResult()) {
Timber.d("Account removed successfully.");
}
} catch (OperationCanceledException | IOException | AuthenticatorException e) {
e.printStackTrace();
@ -234,11 +224,13 @@ public class CommonsApplication extends Application {
if (getIndex() == allAccounts.length) {
Timber.d("All accounts have been removed");
//TODO: fix preference manager
PreferenceManager.getDefaultSharedPreferences(getInstance()).edit().clear().commit();
PreferenceManager.getDefaultSharedPreferences(getInstance())
.edit().clear().commit();
SharedPreferences preferences = context
.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE);
preferences.edit().clear().commit();
context.getSharedPreferences("prefs", Context.MODE_PRIVATE).edit().clear().commit();
context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
.edit().clear().commit();
preferences.edit().putBoolean("firstrun", false).apply();
updateAllDatabases();
currentAccount = null;
@ -265,4 +257,8 @@ public class CommonsApplication extends Application {
Category.Table.onDelete(db);
Contribution.Table.onDelete(db);
}
public interface LogoutListener {
void onLogoutComplete();
}
}

View file

@ -3,10 +3,10 @@ package fr.free.nrw.commons;
import android.support.annotation.Nullable;
public class License {
String key;
String template;
String url;
String name;
private String key;
private String template;
private String url;
private String name;
public License(String key, String template, String url, String name) {
if (key == null) {

View file

@ -10,18 +10,16 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
public class LicenseList {
Map<String, License> licenses = new HashMap<>();
Resources res;
private static String XMLNS_LICENSE = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
private Map<String, License> licenses = new HashMap<>();
private Resources res;
public LicenseList(Activity activity) {
res = activity.getResources();
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses);
while (Utils.xmlFastForward(parser, XMLNS_LICENSE, "license")) {
String namespace = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
while (Utils.xmlFastForward(parser, namespace, "license")) {
String id = parser.getAttributeValue(null, "id");
String template = parser.getAttributeValue(null, "template");
String url = parser.getAttributeValue(null, "url");
@ -31,10 +29,6 @@ public class LicenseList {
}
}
public Set<String> keySet() {
return licenses.keySet();
}
public Collection<License> values() {
return licenses.values();
}
@ -44,7 +38,7 @@ public class LicenseList {
}
@Nullable
public License licenseForTemplate(String template) {
License licenseForTemplate(String template) {
String ucTemplate = new PageTitle(template).getDisplayText();
for (License license : values()) {
if (ucTemplate.equals(new PageTitle(license.getTemplate()).getDisplayText())) {
@ -54,26 +48,16 @@ public class LicenseList {
return null;
}
public String nameIdForTemplate(String template) {
private String nameIdForTemplate(String template) {
// hack :D (converts dashes and periods to underscores)
// cc-by-sa-3.0 -> cc_by_sa_3_0
return "license_name_" + template.toLowerCase(Locale.ENGLISH).replace("-", "_").replace(".", "_");
return "license_name_" + template.toLowerCase(Locale.ENGLISH).replace("-",
"_").replace(".", "_");
}
private int stringIdByName(String stringId) {
return res.getIdentifier("fr.free.nrw.commons:string/" + stringId, null, null);
}
public String nameForTemplate(String template) {
//Log.d("Commons", "LicenseList.nameForTemplate: template: " + template);
String stringId = nameIdForTemplate(template);
//Log.d("Commons", "LicenseList.nameForTemplate: stringId: " + stringId);
int nameId = stringIdByName(stringId);
//Log.d("Commons", "LicenseList.nameForTemplate: nameId: " + nameId);
if(nameId != 0) {
//Log.d("Commons", "LicenseList.nameForTemplate: name: " + name);
return res.getString(nameId);
}
return template;
private String nameForTemplate(String template) {
int nameId = res.getIdentifier("fr.free.nrw.commons:string/"
+ nameIdForTemplate(template), null, null);
return (nameId != 0) ? res.getString(nameId) : template;
}
}

View file

@ -29,12 +29,66 @@ public class Media implements Parcelable {
}
};
private static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE);
// Primary metadata fields
protected Uri localUri;
protected String imageUrl;
protected String filename;
protected String description; // monolingual description on input...
protected long dataLength;
protected Date dateCreated;
protected @Nullable Date dateUploaded;
protected int width;
protected int height;
protected String license;
protected String creator;
protected ArrayList<String> categories; // as loaded at runtime?
private Map<String, String> descriptions; // multilingual descriptions as loaded
private HashMap<String, Object> tags = new HashMap<>();
private @Nullable LatLng coordinates;
protected Media() {
this.categories = new ArrayList<>();
this.descriptions = new HashMap<>();
}
private HashMap<String, Object> tags = new HashMap<>();
public Media(String filename) {
this();
this.filename = filename;
}
public Media(Uri localUri, String imageUrl, String filename, String description,
long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
this();
this.localUri = localUri;
this.imageUrl = imageUrl;
this.filename = filename;
this.description = description;
this.dataLength = dataLength;
this.dateCreated = dateCreated;
this.dateUploaded = dateUploaded;
this.creator = creator;
}
@SuppressWarnings("unchecked")
public Media(Parcel in) {
localUri = in.readParcelable(Uri.class.getClassLoader());
imageUrl = in.readString();
filename = in.readString();
description = in.readString();
dataLength = in.readLong();
dateCreated = (Date) in.readSerializable();
dateUploaded = (Date) in.readSerializable();
creator = in.readString();
tags = (HashMap<String, Object>) in.readSerializable();
width = in.readInt();
height = in.readInt();
license = in.readString();
if (categories != null) {
in.readStringList(categories);
}
descriptions = in.readHashMap(ClassLoader.getSystemClassLoader());
}
public Object getTag(String key) {
return tags.get(key);
@ -44,15 +98,14 @@ public class Media implements Parcelable {
tags.put(key, value);
}
public static Pattern displayTitlePattern = Pattern.compile("(.*)(\\.\\w+)", Pattern.CASE_INSENSITIVE);
public String getDisplayTitle() {
if(filename == null) {
public String getDisplayTitle() {
if (filename == null) {
return "";
}
// FIXME: Gross hack bercause my regex skills suck maybe or I am too lazy who knows
String title = getFilePageTitle().getDisplayText().replaceFirst("^File:", "");
Matcher matcher = displayTitlePattern.matcher(title);
if(matcher.matches()) {
if (matcher.matches()) {
return matcher.group(1);
} else {
return title;
@ -68,7 +121,7 @@ public class Media implements Parcelable {
}
public String getImageUrl() {
if(imageUrl == null) {
if (imageUrl == null) {
imageUrl = Utils.makeThumbBaseUrl(this.getFilename());
}
return imageUrl;
@ -86,6 +139,10 @@ public class Media implements Parcelable {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getDataLength() {
return dataLength;
}
@ -102,7 +159,8 @@ public class Media implements Parcelable {
this.dateCreated = date;
}
public @Nullable Date getDateUploaded() {
public @Nullable
Date getDateUploaded() {
return dateUploaded;
}
@ -138,7 +196,8 @@ public class Media implements Parcelable {
this.license = license;
}
public @Nullable LatLng getCoordinates() {
public @Nullable
LatLng getCoordinates() {
return coordinates;
}
@ -146,24 +205,9 @@ public class Media implements Parcelable {
this.coordinates = coordinates;
}
// Primary metadata fields
protected Uri localUri;
protected String imageUrl;
protected String filename;
protected String description; // monolingual description on input...
protected long dataLength;
protected Date dateCreated;
protected @Nullable Date dateUploaded;
protected int width;
protected int height;
protected String license;
private @Nullable LatLng coordinates;
protected String creator;
protected ArrayList<String> categories; // as loaded at runtime?
protected Map<String, String> descriptions; // multilingual descriptions as loaded
@SuppressWarnings("unchecked")
public ArrayList<String> getCategories() {
return (ArrayList<String>)categories.clone(); // feels dirty
return (ArrayList<String>) categories.clone(); // feels dirty
}
public void setCategories(List<String> categories) {
@ -171,7 +215,7 @@ public class Media implements Parcelable {
this.categories.addAll(categories);
}
public void setDescriptions(Map<String,String> descriptions) {
void setDescriptions(Map<String, String> descriptions) {
for (String key : this.descriptions.keySet()) {
this.descriptions.remove(key);
}
@ -196,23 +240,6 @@ public class Media implements Parcelable {
}
}
public Media(String filename) {
this();
this.filename = filename;
}
public Media(Uri localUri, String imageUrl, String filename, String description, long dataLength, Date dateCreated, @Nullable Date dateUploaded, String creator) {
this();
this.localUri = localUri;
this.imageUrl = imageUrl;
this.filename = filename;
this.description = description;
this.dataLength = dataLength;
this.dateCreated = dateCreated;
this.dateUploaded = dateUploaded;
this.creator = creator;
}
@Override
public int describeContents() {
return 0;
@ -235,27 +262,4 @@ public class Media implements Parcelable {
parcel.writeStringList(categories);
parcel.writeMap(descriptions);
}
public Media(Parcel in) {
localUri = in.readParcelable(Uri.class.getClassLoader());
imageUrl = in.readString();
filename = in.readString();
description = in.readString();
dataLength = in.readLong();
dateCreated = (Date) in.readSerializable();
dateUploaded = (Date) in.readSerializable();
creator = in.readString();
tags = (HashMap<String, Object>)in.readSerializable();
width = in.readInt();
height = in.readInt();
license = in.readString();
if (categories != null) {
in.readStringList(categories);
}
descriptions = in.readHashMap(ClassLoader.getSystemClassLoader());
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -2,7 +2,6 @@ package fr.free.nrw.commons;
import android.support.annotation.Nullable;
import org.mediawiki.api.ApiResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

View file

@ -58,7 +58,7 @@ public class PageTitle {
*/
@NonNull
public Uri getCanonicalUri() {
String uriStr = CommonsApplication.HOME_URL + Uri.encode(getPrefixedText(), ":/");
String uriStr = BuildConfig.HOME_URL + Uri.encode(getPrefixedText(), ":/");
return Uri.parse(uriStr);
}
@ -71,7 +71,7 @@ public class PageTitle {
*/
@NonNull
public Uri getMobileUri() {
String uriStr = CommonsApplication.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
String uriStr = BuildConfig.MOBILE_HOME_URL + Uri.encode(getPrefixedText(), ":/");
return Uri.parse(uriStr);
}

View file

@ -40,7 +40,6 @@ import javax.xml.transform.stream.StreamResult;
import fr.free.nrw.commons.settings.Prefs;
import timber.log.Timber;
public class Utils {
// Get SHA1 of file from input stream
@ -80,10 +79,12 @@ public class Utils {
}
}
/** Fix Html.fromHtml is deprecated problem
/**
* Fix Html.fromHtml is deprecated problem
*
* @param source provided Html string
* @return returned Spanned of appropriate method according to version check
* */
*/
public static Spanned fromHtml(String source) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY);
@ -127,7 +128,7 @@ public class Utils {
public static String makeThumbBaseUrl(String filename) {
String name = new PageTitle(filename).getPrefixedText();
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
return String.format("%s/%s/%s/%s", CommonsApplication.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
}
public static String getStringFromDOM(Node dom) {
@ -241,9 +242,9 @@ public class Utils {
public static boolean xmlFastForward(XmlPullParser parser, String namespace, String element) {
try {
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() == XmlPullParser.START_TAG &&
parser.getNamespace().equals(namespace) &&
parser.getName().equals(element)) {
if (parser.getEventType() == XmlPullParser.START_TAG
&& parser.getNamespace().equals(namespace)
&& parser.getName().equals(element)) {
// We found it!
return true;
}
@ -266,7 +267,8 @@ public class Utils {
extension = "jpg";
}
title = jpegPattern.matcher(title).replaceFirst(".jpg");
if (extension != null && !title.toLowerCase(Locale.getDefault()).endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
if (extension != null && !title.toLowerCase(Locale.getDefault())
.endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
title += "." + extension;
}
return title;
@ -277,6 +279,6 @@ public class Utils {
}
public static boolean isDarkTheme(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme",false);
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false);
}
}

View file

@ -14,7 +14,7 @@ import timber.log.Timber;
public abstract class AuthenticatedActivity extends NavigationBaseActivity {
String accountType;
private String accountType;
CommonsApplication app;
private String authCookie;
@ -28,9 +28,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
.subscribeOn(Schedulers.io())
.doOnError(Timber::e)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
cookie -> onAuthCookieAcquired(cookie),
throwable -> onAuthFailure());
.subscribe(this::onAuthCookieAcquired, throwable -> onAuthFailure());
}
private void addAccount(AccountManager accountManager) {
@ -55,13 +53,13 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
}
protected void requestAuthToken() {
if(authCookie != null) {
if (authCookie != null) {
onAuthCookieAcquired(authCookie);
return;
}
AccountManager accountManager = AccountManager.get(this);
Account curAccount = app.getCurrentAccount();
if(curAccount == null) {
if (curAccount == null) {
addAccount(accountManager);
} else {
getAuthCookie(curAccount, accountManager);
@ -72,7 +70,7 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = CommonsApplication.getInstance();
if(savedInstanceState != null) {
if (savedInstanceState != null) {
authCookie = savedInstanceState.getString("authCookie");
}
}
@ -84,5 +82,6 @@ public abstract class AuthenticatedActivity extends NavigationBaseActivity {
}
protected abstract void onAuthCookieAcquired(String authCookie);
protected abstract void onAuthFailure();
}

View file

@ -8,10 +8,8 @@ import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

View file

@ -6,6 +6,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.theme.BaseActivity;
import timber.log.Timber;
@ -24,16 +25,17 @@ public class SignupActivity extends BaseActivity {
webView.setWebViewClient(new MyWebViewClient());
WebSettings webSettings = webView.getSettings();
//Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can trust Wikimedia's site... right?
/*Needed to refresh Captcha. Might introduce XSS vulnerabilities, but we can
trust Wikimedia's site... right?*/
webSettings.setJavaScriptEnabled(true);
webView.loadUrl("https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes");
webView.loadUrl(BuildConfig.SIGNUP_LANDING_URL);
}
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.equals("https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes")) {
if (url.equals(BuildConfig.SIGNUP_SUCCESS_REDIRECTION_URL)) {
//Signup success, so clear cookies, notify user, and load LoginActivity again
Timber.d("Overriding URL %s", url);

View file

@ -16,21 +16,32 @@ import java.io.IOException;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import static android.accounts.AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION;
import static android.accounts.AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE;
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
import static android.accounts.AccountManager.KEY_AUTHTOKEN;
import static android.accounts.AccountManager.KEY_BOOLEAN_RESULT;
import static android.accounts.AccountManager.KEY_ERROR_CODE;
import static android.accounts.AccountManager.KEY_ERROR_MESSAGE;
import static android.accounts.AccountManager.KEY_INTENT;
import static fr.free.nrw.commons.auth.LoginActivity.PARAM_USERNAME;
public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
private Context context;
public WikiAccountAuthenticator(Context context) {
WikiAccountAuthenticator(Context context) {
super(context);
this.context = context;
}
private Bundle unsupportedOperation() {
Bundle bundle = new Bundle();
bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
bundle.putInt(KEY_ERROR_CODE, ERROR_CODE_UNSUPPORTED_OPERATION);
// HACK: the docs indicate that this is a required key bit it's not displayed to the user.
bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "");
bundle.putString(KEY_ERROR_MESSAGE, "");
return bundle;
}
@ -54,10 +65,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
private Bundle addAccount(AccountAuthenticatorResponse response) {
Intent Intent = new Intent(context, LoginActivity.class);
Intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
Intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, Intent);
bundle.putParcelable(KEY_INTENT, Intent);
return bundle;
}
@ -78,14 +89,16 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
//TODO add 2fa support here
String result = api.login(username, password);
if(result.equals("PASS")) {
if (result.equals("PASS")) {
return api.getAuthCookie();
} else {
return null;
}
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(context);
@ -101,9 +114,9 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
}
if (authCookie != null) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountUtil.accountType());
result.putString(AccountManager.KEY_AUTHTOKEN, authCookie);
result.putString(KEY_ACCOUNT_NAME, account.name);
result.putString(KEY_ACCOUNT_TYPE, AccountUtil.accountType());
result.putString(KEY_AUTHTOKEN, authCookie);
return result;
}
}
@ -112,10 +125,10 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity panel.
final Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra(LoginActivity.PARAM_USERNAME, account.name);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(PARAM_USERNAME, account.name);
intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
bundle.putParcelable(KEY_INTENT, intent);
return bundle;
}
@ -133,7 +146,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
@NonNull Account account, @NonNull String[] features)
throws NetworkErrorException {
Bundle bundle = new Bundle();
bundle.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
bundle.putBoolean(KEY_BOOLEAN_RESULT, false);
return bundle;
}
@ -141,8 +154,7 @@ public class WikiAccountAuthenticator extends AbstractAccountAuthenticator {
@Override
public Bundle updateCredentials(@NonNull AccountAuthenticatorResponse response,
@NonNull Account account, @Nullable String authTokenType,
@Nullable Bundle options)
throws NetworkErrorException {
@Nullable Bundle options) throws NetworkErrorException {
return unsupportedOperation();
}

View file

@ -32,7 +32,7 @@ public class CacheController {
public void cacheCategory() {
List<String> pointCatList = new ArrayList<>();
if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
pointCatList.addAll(MwVolleyApi.getGpsCat());
pointCatList.addAll(MwVolleyApi.getGpsCat());
Timber.d("Categories being cached: %s", pointCatList);
} else {
Timber.d("No categories found, so no categories cached");

View file

@ -31,15 +31,12 @@ class CategoriesRenderer extends Renderer<CategoryItem> {
@Override
protected void hookListeners(View view) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CategoryItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.categoryClicked(item);
}
view.setOnClickListener(v -> {
CategoryItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.categoryClicked(item);
}
});
}

View file

@ -25,6 +25,7 @@ import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@ -36,6 +37,7 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.data.Category;
import fr.free.nrw.commons.upload.MwVolleyApi;
import fr.free.nrw.commons.utils.StringSortingUtils;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@ -97,6 +99,7 @@ public class CategorizationFragment extends Fragment {
categoriesCache = new HashMap<>();
if (savedInstanceState != null) {
items.addAll(savedInstanceState.getParcelableArrayList("currentCategories"));
//noinspection unchecked
categoriesCache.putAll((HashMap<String, ArrayList<String>>) savedInstanceState
.getSerializable("categoriesCache"));
}
@ -106,7 +109,7 @@ public class CategorizationFragment extends Fragment {
RxTextView.textChanges(categoriesFilter)
.takeUntil(RxView.detaches(categoriesFilter))
.debounce(300, TimeUnit.MILLISECONDS)
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(filter -> updateCategoryList(filter.toString()));
return rootView;
@ -194,16 +197,15 @@ public class CategorizationFragment extends Fragment {
.concatWith(
searchAll(filter)
.mergeWith(searchCategories(filter))
.concatWith( TextUtils.isEmpty(filter)
.concatWith(TextUtils.isEmpty(filter)
? defaultCategories() : Observable.empty())
)
.filter(categoryItem -> !containsYear(categoryItem.getName()))
.distinct()
.sorted(sortBySimilarity(filter))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
s -> categoriesAdapter.add(s),
throwable -> Timber.e(throwable),
() -> {
s -> categoriesAdapter.add(s), Timber::e, () -> {
categoriesAdapter.notifyDataSetChanged();
categoriesSearchInProgress.setVisibility(View.GONE);
@ -222,6 +224,12 @@ public class CategorizationFragment extends Fragment {
);
}
private Comparator<CategoryItem> sortBySimilarity(final String filter) {
Comparator<String> stringSimilarityComparator = StringSortingUtils.sortBySimilarity(filter);
return (firstItem, secondItem) -> stringSimilarityComparator
.compare(firstItem.getName(), secondItem.getName());
}
private List<String> getStringList(List<CategoryItem> input) {
List<String> output = new ArrayList<>();
for (CategoryItem item : input) {

View file

@ -15,31 +15,39 @@ import fr.free.nrw.commons.data.Category;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
import static android.content.UriMatcher.NO_MATCH;
import static fr.free.nrw.commons.data.Category.Table.ALL_FIELDS;
import static fr.free.nrw.commons.data.Category.Table.COLUMN_ID;
import static fr.free.nrw.commons.data.Category.Table.TABLE_NAME;
public class CategoryContentProvider extends ContentProvider {
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
// For URI matcher
private static final int CATEGORIES = 1;
private static final int CATEGORIES_ID = 2;
public static final String AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
private static final String BASE_PATH = "categories";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CATEGORIES);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CATEGORIES_ID);
}
private DBOpenHelper dbOpenHelper;
public static Uri uriForId(int id) {
return Uri.parse(BASE_URI.toString() + "/" + id);
}
private DBOpenHelper dbOpenHelper;
@SuppressWarnings("ConstantConditions")
@Override
public boolean onCreate() {
dbOpenHelper = CommonsApplication.getInstance().getDBOpenHelper();
CommonsApplication app = ((CommonsApplication) getContext().getApplicationContext());
dbOpenHelper = app.getDBOpenHelper();
return false;
}
@ -48,23 +56,23 @@ public class CategoryContentProvider extends ContentProvider {
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Category.Table.TABLE_NAME);
queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
Cursor cursor;
switch(uriType) {
switch (uriType) {
case CATEGORIES:
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case CATEGORIES_ID:
cursor = queryBuilder.query(db,
Category.Table.ALL_FIELDS,
ALL_FIELDS,
"_id = ?",
new String[] { uri.getLastPathSegment() },
new String[]{uri.getLastPathSegment()},
null,
null,
sortOrder
@ -92,7 +100,7 @@ public class CategoryContentProvider extends ContentProvider {
long id;
switch (uriType) {
case CATEGORIES:
id = sqlDB.insert(Category.Table.TABLE_NAME, null, contentValues);
id = sqlDB.insert(TABLE_NAME, null, contentValues);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
@ -115,9 +123,9 @@ public class CategoryContentProvider extends ContentProvider {
sqlDB.beginTransaction();
switch (uriType) {
case CATEGORIES:
for(ContentValues value: values) {
for (ContentValues value : values) {
Timber.d("Inserting! %s", value);
sqlDB.insert(Category.Table.TABLE_NAME, null, value);
sqlDB.insert(TABLE_NAME, null, value);
}
break;
default:
@ -134,24 +142,25 @@ public class CategoryContentProvider extends ContentProvider {
public int update(@NonNull Uri uri, ContentValues contentValues, String selection,
String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
SQL Injection warnings: First, note that we're not exposing this to the
outside world (exported="false"). Even then, we should make sure to sanitize
all user input appropriately. Input that passes through ContentValues
should be fine. So only issues are those that pass in via concating.
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
In here, the only concat created argument is for id. It is cast to an int,
and will error out otherwise.
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
int rowsUpdated;
switch (uriType) {
case CATEGORIES_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Category.Table.TABLE_NAME,
int id = Integer.valueOf(uri.getLastPathSegment());
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
Category.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
COLUMN_ID + " = ?",
new String[]{String.valueOf(id)});
} else {
throw new IllegalArgumentException(
"Parameter `selection` should be empty when updating an ID");

View file

@ -127,7 +127,7 @@ public class Contribution extends Media {
}
public String getPageContents() {
StringBuffer buffer = new StringBuffer();
StringBuilder buffer = new StringBuilder();
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
buffer

View file

@ -1,30 +1,39 @@
package fr.free.nrw.commons.contributions;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.FileProvider;
import java.io.File;
import java.util.Date;
import java.util.List;
import fr.free.nrw.commons.upload.ShareActivity;
import fr.free.nrw.commons.upload.UploadService;
import timber.log.Timber;
public class ContributionController {
import static android.content.Intent.ACTION_GET_CONTENT;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.EXTRA_STREAM;
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE;
class ContributionController {
private static final int SELECT_FROM_GALLERY = 1;
private static final int SELECT_FROM_CAMERA = 2;
private Fragment fragment;
private Activity activity;
private final static int SELECT_FROM_GALLERY = 1;
private final static int SELECT_FROM_CAMERA = 2;
public ContributionController(Fragment fragment) {
ContributionController(Fragment fragment) {
this.fragment = fragment;
this.activity = fragment.getActivity();
}
// See http://stackoverflow.com/a/5054673/17865 for why this is done
@ -34,43 +43,59 @@ public class ContributionController {
File photoFile = new File(fragment.getContext().getCacheDir() + "/images",
new Date().getTime() + ".jpg");
photoFile.getParentFile().mkdirs();
Context applicationContext = fragment.getActivity().getApplicationContext();
return FileProvider.getUriForFile(
fragment.getContext(),
fragment.getActivity().getApplicationContext().getPackageName() + ".provider",
applicationContext.getPackageName() + ".provider",
photoFile);
}
public void startCameraCapture() {
private static void requestWritePermission(Context context, Intent intent, Uri uri) {
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
void startCameraCapture() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache();
takePictureIntent.setFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Intent.setFlags doesn't work for API level <20
requestWritePermission(fragment.getContext(), takePictureIntent, lastGeneratedCaptureUri);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureUri);
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
}
public void startGalleryPick() {
//FIXME: Starts gallery (opens Google Photos)
Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
Intent pickImageIntent = new Intent(ACTION_GET_CONTENT);
pickImageIntent.setType("image/*");
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
}
public void handleImagePicked(int requestCode, Intent data) {
void handleImagePicked(int requestCode, Intent data) {
FragmentActivity activity = fragment.getActivity();
Intent shareIntent = new Intent(activity, ShareActivity.class);
shareIntent.setAction(Intent.ACTION_SEND);
switch(requestCode) {
shareIntent.setAction(ACTION_SEND);
switch (requestCode) {
case SELECT_FROM_GALLERY:
//Handles image picked from gallery
Uri imageData = data.getData();
shareIntent.setType(activity.getContentResolver().getType(imageData));
shareIntent.putExtra(Intent.EXTRA_STREAM, imageData);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_GALLERY);
shareIntent.putExtra(EXTRA_STREAM, imageData);
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
break;
case SELECT_FROM_CAMERA:
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
shareIntent.putExtra(Intent.EXTRA_STREAM, lastGeneratedCaptureUri);
shareIntent.putExtra(UploadService.EXTRA_SOURCE, Contribution.SOURCE_CAMERA);
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
break;
}
Timber.i("Image selected");
@ -81,12 +106,14 @@ public class ContributionController {
}
}
public void saveState(Bundle outState) {
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
void saveState(Bundle outState) {
if (outState != null) {
outState.putParcelable("lastGeneratedCaptureURI", lastGeneratedCaptureUri);
}
}
public void loadState(Bundle savedInstanceState) {
if(savedInstanceState != null) {
void loadState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
}
}

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.contributions;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
@ -38,13 +37,17 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
public class ContributionsActivity
extends AuthenticatedActivity
implements LoaderManager.LoaderCallbacks<Cursor>,
AdapterView.OnItemClickListener,
MediaDetailPagerFragment.MediaDetailProvider,
FragmentManager.OnBackStackChangedListener,
ContributionsListFragment.SourceRefresher {
import static android.content.ContentResolver.requestSync;
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUTHORITY;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
public class ContributionsActivity extends AuthenticatedActivity
implements LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener,
MediaDetailPagerFragment.MediaDetailProvider, FragmentManager.OnBackStackChangedListener,
ContributionsListFragment.SourceRefresher {
private Cursor allContributions;
private ContributionsListFragment contributionsList;
@ -63,14 +66,18 @@ public class ContributionsActivity
This is why Contribution.STATE_COMPLETED is -1.
*/
private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, " + Contribution.Table.COLUMN_UPLOADED + " DESC , (" + Contribution.Table.COLUMN_TIMESTAMP + " * " + Contribution.Table.COLUMN_STATE + ")";
private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, "
+ Contribution.Table.COLUMN_UPLOADED + " DESC , ("
+ Contribution.Table.COLUMN_TIMESTAMP + " * "
+ Contribution.Table.COLUMN_STATE + ")";
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder binder) {
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder)binder).getService();
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder)
.getService();
isUploadServiceConnected = true;
}
@ -86,7 +93,7 @@ public class ContributionsActivity
compositeDisposable.clear();
getSupportFragmentManager().removeOnBackStackChangedListener(this);
super.onDestroy();
if(isUploadServiceConnected) {
if (isUploadServiceConnected) {
unbindService(uploadServiceConnection);
}
}
@ -96,9 +103,9 @@ public class ContributionsActivity
super.onResume();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean isSettingsChanged =
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false);
sharedPreferences.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,false);
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false);
editor.apply();
if (isSettingsChanged) {
refreshSource();
@ -107,14 +114,16 @@ public class ContributionsActivity
@Override
protected void onAuthCookieAcquired(String authCookie) {
// Do a sync everytime we get here!
ContentResolver.requestSync(CommonsApplication.getInstance().getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
// Do a sync every time we get here!
CommonsApplication app = ((CommonsApplication) getApplication());
requestSync(app.getCurrentAccount(), AUTHORITY, new Bundle());
Intent uploadServiceIntent = new Intent(this, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
startService(uploadServiceIntent);
bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
allContributions = getContentResolver().query(ContributionsContentProvider.BASE_URI, Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
allContributions = getContentResolver().query(BASE_URI, ALL_FIELDS,
CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
getSupportLoaderManager().initLoader(0, null, this);
}
@ -127,12 +136,13 @@ public class ContributionsActivity
// Activity can call methods in the fragment by acquiring a
// reference to the Fragment from FragmentManager, using findFragmentById()
contributionsList = (ContributionsListFragment)getSupportFragmentManager()
FragmentManager supportFragmentManager = getSupportFragmentManager();
contributionsList = (ContributionsListFragment) supportFragmentManager
.findFragmentById(R.id.contributionsListFragment);
getSupportFragmentManager().addOnBackStackChangedListener(this);
supportFragmentManager.addOnBackStackChangedListener(this);
if (savedInstanceState != null) {
mediaDetails = (MediaDetailPagerFragment)getSupportFragmentManager()
mediaDetails = (MediaDetailPagerFragment) supportFragmentManager
.findFragmentById(R.id.contributionsFragmentContainer);
}
requestAuthToken();
@ -143,21 +153,25 @@ public class ContributionsActivity
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("mediaDetailsVisible", (mediaDetails != null && mediaDetails.isVisible()));
boolean mediaDetailsVisible = mediaDetails != null && mediaDetails.isVisible();
outState.putBoolean("mediaDetailsVisible", mediaDetailsVisible);
}
/** Replace whatever is in the current contributionsFragmentContainer view with mediaDetailPagerFragment,
/ and preserve previous state in back stack.
/ Called when user selects a contribution. */
/**
* Replace whatever is in the current contributionsFragmentContainer view with
* mediaDetailPagerFragment, and preserve previous state in back stack.
* Called when user selects a contribution.
*/
private void showDetail(int i) {
if(mediaDetails == null ||!mediaDetails.isVisible()) {
if (mediaDetails == null || !mediaDetails.isVisible()) {
mediaDetails = new MediaDetailPagerFragment();
this.getSupportFragmentManager()
FragmentManager supportFragmentManager = getSupportFragmentManager();
supportFragmentManager
.beginTransaction()
.replace(R.id.contributionsFragmentContainer, mediaDetails)
.addToBackStack(null)
.commit();
this.getSupportFragmentManager().executePendingTransactions();
supportFragmentManager.executePendingTransactions();
}
mediaDetails.showImage(i);
}
@ -165,7 +179,7 @@ public class ContributionsActivity
public void retryUpload(int i) {
allContributions.moveToPosition(i);
Contribution c = Contribution.fromCursor(allContributions);
if(c.getState() == Contribution.STATE_FAILED) {
if (c.getState() == STATE_FAILED) {
uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c);
Timber.d("Restarting for %s", c.toContentValues());
} else {
@ -176,9 +190,9 @@ public class ContributionsActivity
public void deleteUpload(int i) {
allContributions.moveToPosition(i);
Contribution c = Contribution.fromCursor(allContributions);
if(c.getState() == Contribution.STATE_FAILED) {
if (c.getState() == STATE_FAILED) {
Timber.d("Deleting failed contrib %s", c.toContentValues());
c.setContentProviderClient(getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY));
c.setContentProviderClient(getContentResolver().acquireContentProviderClient(AUTHORITY));
c.delete();
} else {
Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues());
@ -187,9 +201,9 @@ public class ContributionsActivity
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
switch (item.getItemId()) {
case android.R.id.home:
if(mediaDetails.isVisible()) {
if (mediaDetails.isVisible()) {
getSupportFragmentManager().popBackStack();
}
return true;
@ -215,21 +229,20 @@ public class ContributionsActivity
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
SharedPreferences sharedPref =
PreferenceManager.getDefaultSharedPreferences(this);
int uploads = sharedPref.getInt(Prefs.UPLOADS_SHOWING, 100);
return new CursorLoader(this, ContributionsContentProvider.BASE_URI,
Contribution.Table.ALL_FIELDS, CONTRIBUTION_SELECTION, null,
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
int uploads = sharedPref.getInt(UPLOADS_SHOWING, 100);
return new CursorLoader(this, BASE_URI,
ALL_FIELDS, CONTRIBUTION_SELECTION, null,
CONTRIBUTION_SORT + "LIMIT " + uploads);
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
if(contributionsList.getAdapter() == null) {
contributionsList
.setAdapter(new ContributionsListAdapter(getApplicationContext(), cursor, 0));
if (contributionsList.getAdapter() == null) {
contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(),
cursor, 0));
} else {
((CursorAdapter)contributionsList.getAdapter()).swapCursor(cursor);
((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor);
}
setUploadCount();
@ -249,34 +262,32 @@ public class ContributionsActivity
if (contributionsList.getAdapter() == null) {
// not yet ready to return data
return null;
} else {
} else {
return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i));
}
}
@Override
public int getTotalMediaCount() {
if(contributionsList.getAdapter() == null) {
if (contributionsList.getAdapter() == null) {
return 0;
}
return contributionsList.getAdapter().getCount();
}
@SuppressWarnings("ConstantConditions")
private void setUploadCount() {
CommonsApplication application = CommonsApplication.getInstance();
CommonsApplication app = ((CommonsApplication) getApplication());
compositeDisposable.add(
CommonsApplication.getInstance().getMWApi()
.getUploadCount(application.getCurrentAccount().name)
app.getMWApi()
.getUploadCount(app.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
uploadCount ->
getSupportActionBar().setSubtitle(getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount,
uploadCount)),
throwable -> Timber.e(throwable, "Fetching upload count failed")
uploadCount -> getSupportActionBar().setSubtitle(getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount, uploadCount)),
t -> Timber.e(t, "Fetching upload count failed")
)
);
}
@ -332,8 +343,7 @@ public class ContributionsActivity
}
public static void startYourself(Context context) {
Intent contributionsIntent = new Intent(context, ContributionsActivity.class);
context.startActivity(contributionsIntent);
context.startActivity(new Intent(context, ContributionsActivity.class));
}
}

View file

@ -13,17 +13,20 @@ import android.text.TextUtils;
import fr.free.nrw.commons.CommonsApplication;
import timber.log.Timber;
public class ContributionsContentProvider extends ContentProvider{
import static android.content.UriMatcher.NO_MATCH;
import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
import static fr.free.nrw.commons.contributions.Contribution.Table.TABLE_NAME;
public class ContributionsContentProvider extends ContentProvider {
private static final int CONTRIBUTIONS = 1;
private static final int CONTRIBUTIONS_ID = 2;
public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
private static final String BASE_PATH = "contributions";
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS);
uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
@ -38,25 +41,29 @@ public class ContributionsContentProvider extends ContentProvider{
return false;
}
@SuppressWarnings("ConstantConditions")
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(Contribution.Table.TABLE_NAME);
queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase();
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase db = app.getDBOpenHelper().getReadableDatabase();
Cursor cursor;
switch(uriType) {
switch (uriType) {
case CONTRIBUTIONS:
cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
cursor = queryBuilder.query(db, projection, selection, selectionArgs,
null, null, sortOrder);
break;
case CONTRIBUTIONS_ID:
cursor = queryBuilder.query(db,
Contribution.Table.ALL_FIELDS,
ALL_FIELDS,
"_id = ?",
new String[] { uri.getLastPathSegment() },
new String[]{uri.getLastPathSegment()},
null,
null,
sortOrder
@ -76,14 +83,16 @@ public class ContributionsContentProvider extends ContentProvider{
return null;
}
@SuppressWarnings("ConstantConditions")
@Override
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
long id = 0;
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
long id;
switch (uriType) {
case CONTRIBUTIONS:
id = sqlDB.insert(Contribution.Table.TABLE_NAME, null, contentValues);
id = sqlDB.insert(TABLE_NAME, null, contentValues);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
@ -92,19 +101,21 @@ public class ContributionsContentProvider extends ContentProvider{
return Uri.parse(BASE_URI + "/" + id);
}
@SuppressWarnings("ConstantConditions")
@Override
public int delete(@NonNull Uri uri, String s, String[] strings) {
int rows = 0;
int rows;
int uriType = uriMatcher.match(uri);
SQLiteDatabase db = CommonsApplication.getInstance().getDBOpenHelper().getReadableDatabase();
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
switch(uriType) {
switch (uriType) {
case CONTRIBUTIONS_ID:
Timber.d("Deleting contribution id %s", uri.getLastPathSegment());
rows = db.delete(Contribution.Table.TABLE_NAME,
rows = sqlDB.delete(TABLE_NAME,
"_id = ?",
new String[] { uri.getLastPathSegment() }
new String[]{uri.getLastPathSegment()}
);
break;
default:
@ -114,17 +125,19 @@ public class ContributionsContentProvider extends ContentProvider{
return rows;
}
@SuppressWarnings("ConstantConditions")
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
Timber.d("Hello, bulk insert! (ContributionsContentProvider)");
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
sqlDB.beginTransaction();
switch (uriType) {
case CONTRIBUTIONS:
for(ContentValues value: values) {
for (ContentValues value : values) {
Timber.d("Inserting! %s", value);
sqlDB.insert(Contribution.Table.TABLE_NAME, null, value);
sqlDB.insert(TABLE_NAME, null, value);
}
break;
default:
@ -136,35 +149,37 @@ public class ContributionsContentProvider extends ContentProvider{
return values.length;
}
@SuppressWarnings("ConstantConditions")
@Override
public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) {
/*
SQL Injection warnings: First, note that we're not exposing this to the outside world (exported="false")
Even then, we should make sure to sanitize all user input appropriately. Input that passes through ContentValues
should be fine. So only issues are those that pass in via concating.
Even then, we should make sure to sanitize all user input appropriately.
Input that passes through ContentValuesshould be fine. So only issues are those that pass
in via concating.
In here, the only concat created argument is for id. It is cast to an int, and will error out otherwise.
In here, the only concat created argument is for id. It is cast to an int, and will
error out otherwise.
*/
int uriType = uriMatcher.match(uri);
SQLiteDatabase sqlDB = CommonsApplication.getInstance().getDBOpenHelper().getWritableDatabase();
int rowsUpdated = 0;
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
SQLiteDatabase sqlDB = app.getDBOpenHelper().getWritableDatabase();
int rowsUpdated;
switch (uriType) {
case CONTRIBUTIONS:
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME,
contentValues,
selection,
selectionArgs);
rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
break;
case CONTRIBUTIONS_ID:
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(Contribution.Table.TABLE_NAME,
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
Contribution.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
new String[]{String.valueOf(id)});
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
throw new IllegalArgumentException(
"Parameter `selection` should be empty when updating an ID");
}
break;
default:

View file

@ -1,13 +1,11 @@
package fr.free.nrw.commons.contributions;
import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
@ -31,18 +29,20 @@ import fr.free.nrw.commons.nearby.NearbyActivity;
import timber.log.Timber;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.app.Activity.RESULT_OK;
import static android.content.Context.MODE_PRIVATE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.view.View.GONE;
public class ContributionsListFragment extends Fragment {
public interface SourceRefresher {
void refreshSource();
}
@BindView(R.id.contributionsList) GridView contributionsList;
@BindView(R.id.waitingMessage) TextView waitingMessage;
@BindView(R.id.emptyMessage) TextView emptyMessage;
@BindView(R.id.contributionsList)
GridView contributionsList;
@BindView(R.id.waitingMessage)
TextView waitingMessage;
@BindView(R.id.emptyMessage)
TextView emptyMessage;
private ContributionController controller;
@Override
@ -50,21 +50,21 @@ public class ContributionsListFragment extends Fragment {
View v = inflater.inflate(R.layout.fragment_contributions, container, false);
ButterKnife.bind(this, v);
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity());
if(savedInstanceState != null) {
contributionsList.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
if (savedInstanceState != null) {
Timber.d("Scrolling to %d", savedInstanceState.getInt("grid-position"));
contributionsList.setSelection(savedInstanceState.getInt("grid-position"));
}
//TODO: Should this be in onResume?
SharedPreferences prefs = this.getActivity().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences prefs = getActivity().getSharedPreferences("prefs", MODE_PRIVATE);
String lastModified = prefs.getString("lastSyncTimestamp", "");
Timber.d("Last Sync Timestamp: %s", lastModified);
if (lastModified.equals("")) {
waitingMessage.setVisibility(View.VISIBLE);
} else {
waitingMessage.setVisibility(View.GONE);
waitingMessage.setVisibility(GONE);
}
return v;
@ -93,7 +93,7 @@ public class ContributionsListFragment extends Fragment {
//FIXME: must get the file data for Google Photos when receive the intent answer, in the onActivityResult method
super.onActivityResult(requestCode, resultCode, data);
if ( resultCode == RESULT_OK ) {
if (resultCode == RESULT_OK) {
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
controller.handleImagePicked(requestCode, data);
@ -105,7 +105,7 @@ public class ContributionsListFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
switch (item.getItemId()) {
case R.id.menu_from_gallery:
//Gallery crashes before reach ShareActivity screen so must implement permissions check here
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -113,7 +113,7 @@ public class ContributionsListFragment extends Fragment {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(),
READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
!= PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
@ -123,7 +123,7 @@ public class ContributionsListFragment extends Fragment {
// sees the explanation, try again to request the permission.
new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.storage_permission_rationale))
.setMessage(getString(R.string.read_storage_permission_rationale))
.setPositiveButton("OK", (dialog, which) -> {
requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
dialog.dismiss();
@ -155,7 +155,42 @@ public class ContributionsListFragment extends Fragment {
return true;
case R.id.menu_from_camera:
controller.startCameraCapture();
SharedPreferences sharedPref = PreferenceManager
.getDefaultSharedPreferences(CommonsApplication.getInstance());
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useExtStorage) {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(getActivity(), WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
new AlertDialog.Builder(getActivity())
.setMessage(getString(R.string.write_storage_permission_rationale))
.setPositiveButton("OK", (dialog, which) -> {
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 3);
dialog.dismiss();
})
.setNegativeButton("Cancel", null)
.create()
.show();
} else {
// No explanation needed, we can request the permission.
requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
3);
// MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
controller.startCameraCapture();
return true;
}
} else {
controller.startCameraCapture();
return true;
}
return true;
default:
return super.onOptionsItemSelected(item);
@ -163,14 +198,15 @@ public class ContributionsListFragment extends Fragment {
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
Timber.d("onRequestPermissionsResult: req code = " + " perm = "
+ permissions + " grant =" + grantResults);
switch (requestCode) {
// 1 = Storage allowed when gallery selected
case 1: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Call controller.startGalleryPick()");
controller.startGalleryPick();
}
@ -178,13 +214,19 @@ public class ContributionsListFragment extends Fragment {
break;
// 2 = Location allowed when 'nearby places' selected
case 2: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
Timber.d("Location permission granted");
Intent nearbyIntent = new Intent(getActivity(), NearbyActivity.class);
startActivity(nearbyIntent);
}
}
break;
case 3: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Timber.d("Call controller.startCameraCapture()");
controller.startCameraCapture();
}
}
}
}
@ -193,7 +235,8 @@ public class ContributionsListFragment extends Fragment {
menu.clear(); // See http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_contributions_list, menu);
if (!CommonsApplication.getInstance().deviceHasCamera()) {
CommonsApplication app = (CommonsApplication) getContext().getApplicationContext();
if (!app.deviceHasCamera()) {
menu.findItem(R.id.menu_from_camera).setEnabled(false);
}
}
@ -217,6 +260,10 @@ public class ContributionsListFragment extends Fragment {
}
protected void clearSyncMessage() {
waitingMessage.setVisibility(View.GONE);
waitingMessage.setVisibility(GONE);
}
public interface SourceRefresher {
void refreshSource();
}
}

View file

@ -23,8 +23,18 @@ import fr.free.nrw.commons.mwapi.LogEventResult;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
import static android.content.Context.MODE_PRIVATE;
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
private static final String[] existsQuery = {COLUMN_FILENAME};
private static final String existsSelection = COLUMN_FILENAME + " = ?";
private static final ContentValues[] EMPTY = {};
private static int COMMIT_THRESHOLD = 10;
public ContributionsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
@ -36,39 +46,38 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
return limit; // FIXME: Parameterize!
}
private static final String[] existsQuery = { Contribution.Table.COLUMN_FILENAME };
private static final String existsSelection = Contribution.Table.COLUMN_FILENAME + " = ?";
private boolean fileExists(ContentProviderClient client, String filename) {
Cursor cursor = null;
try {
cursor = client.query(ContributionsContentProvider.BASE_URI,
cursor = client.query(BASE_URI,
existsQuery,
existsSelection,
new String[] { filename },
new String[]{filename},
""
);
return cursor.getCount() != 0;
return cursor != null && cursor.getCount() != 0;
} catch (RemoteException e) {
throw new RuntimeException(e);
} finally {
if ( cursor != null ) {
if (cursor != null) {
cursor.close();
}
}
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
public void onPerformSync(Account account, Bundle bundle, String authority,
ContentProviderClient contentProviderClient, SyncResult syncResult) {
// This code is fraught with possibilities of race conditions, but lalalalala I can't hear you!
String user = account.name;
MediaWikiApi api = CommonsApplication.getInstance().getMWApi();
SharedPreferences prefs = this.getContext().getSharedPreferences("prefs", Context.MODE_PRIVATE);
SharedPreferences prefs = getContext().getSharedPreferences("prefs", MODE_PRIVATE);
String lastModified = prefs.getString("lastSyncTimestamp", "");
Date curTime = new Date();
LogEventResult result;
Boolean done = false;
String queryContinue = null;
while(!done) {
while (!done) {
try {
result = api.logEvents(user, lastModified, queryContinue, getLimit());
@ -90,19 +99,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
continue;
}
String filename = image.getFilename();
if(fileExists(contentProviderClient, filename)) {
if (fileExists(contentProviderClient, filename)) {
Timber.d("Skipping %s", filename);
continue;
}
String thumbUrl = Utils.makeThumbBaseUrl(filename);
Date dateUpdated = image.getDateUpdated();
Contribution contrib = new Contribution(null, thumbUrl, filename, "", -1, dateUpdated, dateUpdated, user, "", "");
contrib.setState(Contribution.STATE_COMPLETED);
Contribution contrib = new Contribution(null, thumbUrl, filename,
"", -1, dateUpdated, dateUpdated, user,
"", "");
contrib.setState(STATE_COMPLETED);
imageValues.add(contrib.toContentValues());
if(imageValues.size() % COMMIT_THRESHOLD == 0) {
if (imageValues.size() % COMMIT_THRESHOLD == 0) {
try {
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{}));
contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@ -110,20 +121,21 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
}
}
if(imageValues.size() != 0) {
if (imageValues.size() != 0) {
try {
contentProviderClient.bulkInsert(ContributionsContentProvider.BASE_URI, imageValues.toArray(new ContentValues[]{}));
contentProviderClient.bulkInsert(BASE_URI, imageValues.toArray(EMPTY));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
queryContinue = result.getQueryContinue();
if(TextUtils.isEmpty(queryContinue)) {
if (TextUtils.isEmpty(queryContinue)) {
done = true;
}
}
prefs.edit().putString("lastSyncTimestamp", Utils.toMWDate(curTime)).apply();
Timber.d("Oh hai, everyone! Look, a kitty!");
}
}

View file

@ -276,13 +276,13 @@ public class MediaDetailFragment extends Fragment {
categoryContainer.removeAllViews();
// @fixme add the category items
for (String cat : categoryNames) {
View catLabel = buildCatLabel(cat);
View catLabel = buildCatLabel(cat, categoryContainer);
categoryContainer.addView(catLabel);
}
}
private View buildCatLabel(final String catName) {
final View item = getLayoutInflater(null).inflate(R.layout.detail_category_item, null, false);
private View buildCatLabel(final String catName, ViewGroup categoryContainer) {
final View item = LayoutInflater.from(getContext()).inflate(R.layout.detail_category_item, categoryContainer, false);
final CompatTextView textView = (CompatTextView)item.findViewById(R.id.mediaDetailCategoryItemText);
textView.setText(catName);

View file

@ -1,11 +1,8 @@
package fr.free.nrw.commons.media;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Build;
@ -34,23 +31,16 @@ import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.mwapi.EventLog;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.Context.DOWNLOAD_SERVICE;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static fr.free.nrw.commons.CommonsApplication.EVENT_SHARE_ATTEMPT;
public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPageChangeListener {
public interface MediaDetailProvider {
Media getMediaAtPosition(int i);
int getTotalMediaCount();
void notifyDatasetChanged();
void registerDataSetObserver(DataSetObserver observer);
void unregisterDataSetObserver(DataSetObserver observer);
}
private ViewPager pager;
private Boolean editable;
private CommonsApplication app;
public MediaDetailPagerFragment() {
this(false);
@ -61,30 +51,10 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
this.editable = editable;
}
//FragmentStatePagerAdapter allows user to swipe across collection of images (no. of images undetermined)
private class MediaDetailAdapter extends FragmentStatePagerAdapter {
public MediaDetailAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
if (i == 0) {
// See bug https://code.google.com/p/android/issues/detail?id=27526
pager.postDelayed(() -> getActivity().supportInvalidateOptionsMenu(), 5);
}
return MediaDetailFragment.forMedia(i, editable);
}
@Override
public int getCount() {
return ((MediaDetailProvider)getActivity()).getTotalMediaCount();
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_media_detail_pager, container, false);
pager = (ViewPager) view.findViewById(R.id.mediaDetailsPager);
pager.addOnPageChangeListener(this);
@ -120,18 +90,18 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
if (savedInstanceState != null) {
editable = savedInstanceState.getBoolean("editable");
}
app = CommonsApplication.getInstance();
setHasOptionsMenu(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
MediaDetailProvider provider = (MediaDetailProvider)getActivity();
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
switch(item.getItemId()) {
switch (item.getItemId()) {
case R.id.menu_share_current_image:
// Share - this is just logs it, intent set in onCreateOptionsMenu, around line 252
EventLog.schema(CommonsApplication.EVENT_SHARE_ATTEMPT)
CommonsApplication app = (CommonsApplication) getActivity().getApplication();
EventLog.schema(EVENT_SHARE_ATTEMPT)
.param("username", app.getCurrentAccount().name)
.param("filename", m.getFilename())
.log();
@ -139,7 +109,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
case R.id.menu_browser_current_image:
// View in browser
Intent viewIntent = new Intent();
viewIntent.setAction(Intent.ACTION_VIEW);
viewIntent.setAction(ACTION_VIEW);
viewIntent.setData(m.getFilePageTitle().getMobileUri());
startActivity(viewIntent);
return true;
@ -149,12 +119,12 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
return true;
case R.id.menu_retry_current_image:
// Retry
((ContributionsActivity)getActivity()).retryUpload(pager.getCurrentItem());
((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem());
getActivity().getSupportFragmentManager().popBackStack();
return true;
case R.id.menu_cancel_current_image:
// todo: delete image
((ContributionsActivity)getActivity()).deleteUpload(pager.getCurrentItem());
((ContributionsActivity) getActivity()).deleteUpload(pager.getCurrentItem());
getActivity().getSupportFragmentManager().popBackStack();
return true;
default:
@ -170,7 +140,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
*/
private void downloadMedia(Media m) {
String imageUrl = m.getImageUrl(),
fileName = m.getFilename();
fileName = m.getFilename();
// Strip 'File:' from beginning of filename, we really shouldn't store it
fileName = fileName.replaceFirst("^File:", "");
Uri imageUri = Uri.parse(imageUrl);
@ -185,14 +155,15 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
req.allowScanningByMediaScanner();
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
Snackbar.make(getView(), R.string.storage_permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, view -> ActivityCompat.requestPermissions(getActivity(),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1)).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& !(ContextCompat.checkSelfPermission(getContext(),
READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
Snackbar.make(getView(), R.string.read_storage_permission_rationale,
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
view -> ActivityCompat.requestPermissions(getActivity(),
new String[]{READ_EXTERNAL_STORAGE}, 1)).show();
} else {
final DownloadManager manager = (DownloadManager)getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(req);
((DownloadManager) getActivity().getSystemService(DOWNLOAD_SERVICE)).enqueue(req);
}
}
@ -202,7 +173,7 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
menu.clear(); // see http://stackoverflow.com/a/8495697/17865
inflater.inflate(R.menu.fragment_image_detail, menu);
if (pager != null) {
MediaDetailProvider provider = (MediaDetailProvider)getActivity();
MediaDetailProvider provider = (MediaDetailProvider) getActivity();
Media m = provider.getMediaAtPosition(pager.getCurrentItem());
if (m != null) {
// Enable default set of actions, then re-enable different set of actions only if it is a failed contrib
@ -225,8 +196,8 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
}
if (m instanceof Contribution) {
Contribution c = (Contribution)m;
switch(c.getState()) {
Contribution c = (Contribution) m;
switch (c.getState()) {
case Contribution.STATE_FAILED:
menu.findItem(R.id.menu_retry_current_image).setEnabled(true).setVisible(true);
menu.findItem(R.id.menu_cancel_current_image).setEnabled(true).setVisible(true);
@ -267,6 +238,39 @@ public class MediaDetailPagerFragment extends Fragment implements ViewPager.OnPa
@Override
public void onPageScrollStateChanged(int i) {
}
public interface MediaDetailProvider {
Media getMediaAtPosition(int i);
int getTotalMediaCount();
void notifyDatasetChanged();
void registerDataSetObserver(DataSetObserver observer);
void unregisterDataSetObserver(DataSetObserver observer);
}
//FragmentStatePagerAdapter allows user to swipe across collection of images (no. of images undetermined)
private class MediaDetailAdapter extends FragmentStatePagerAdapter {
public MediaDetailAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
if (i == 0) {
// See bug https://code.google.com/p/android/issues/detail?id=27526
pager.postDelayed(() -> getActivity().supportInvalidateOptionsMenu(), 5);
}
return MediaDetailFragment.forMedia(i, editable);
}
@Override
public int getCount() {
return ((MediaDetailProvider) getActivity()).getTotalMediaCount();
}
}
}

View file

@ -33,7 +33,7 @@ public class CategoryModifier extends PageModifier {
JSONArray categories;
categories = params.optJSONArray(PARAM_CATEGORIES);
StringBuffer categoriesString = new StringBuffer();
StringBuilder categoriesString = new StringBuilder();
for(int i=0; i < categories.length(); i++) {
String category = categories.optString(i);
categoriesString.append("\n[[Category:").append(category).append("]]");

View file

@ -27,7 +27,7 @@ public class ModifierSequence {
public ModifierSequence(Uri mediaUri, JSONObject data) {
this(mediaUri);
JSONArray modifiersJSON = data.optJSONArray("modifiers");
for(int i=0; i< modifiersJSON.length(); i++) {
for (int i=0; i< modifiersJSON.length(); i++) {
modifiers.add(PageModifier.fromJSON(modifiersJSON.optJSONObject(i)));
}
}
@ -41,14 +41,14 @@ public class ModifierSequence {
}
public String executeModifications(String pageName, String pageContents) {
for(PageModifier modifier: modifiers) {
for (PageModifier modifier: modifiers) {
pageContents = modifier.doModification(pageName, pageContents);
}
return pageContents;
}
public String getEditSummary() {
StringBuffer editSummary = new StringBuffer();
StringBuilder editSummary = new StringBuilder();
for(PageModifier modifier: modifiers) {
editSummary.append(modifier.getEditSumary()).append(" ");
}
@ -60,7 +60,7 @@ public class ModifierSequence {
JSONObject data = new JSONObject();
try {
JSONArray modifiersJSON = new JSONArray();
for(PageModifier modifier: modifiers) {
for (PageModifier modifier: modifiers) {
modifiersJSON.put(modifier.toJSON());
}
data.put("modifiers", modifiersJSON);
@ -81,7 +81,8 @@ public class ModifierSequence {
// Hardcoding column positions!
ModifierSequence ms = null;
try {
ms = new ModifierSequence(Uri.parse(cursor.getString(1)), new JSONObject(cursor.getString(2)));
ms = new ModifierSequence(Uri.parse(cursor.getString(1)),
new JSONObject(cursor.getString(2)));
} catch (JSONException e) {
throw new RuntimeException(e);
}

View file

@ -30,6 +30,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.PageTitle;
@ -235,8 +236,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
}
return categories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
}).flatMapObservable(Observable::fromIterable);
}
@Override
@ -265,15 +265,14 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
}
return categories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
}).flatMapObservable(Observable::fromIterable);
}
@Override
@NonNull
public Observable<String> searchTitles(String title, int searchCatsLimit) {
return Single.fromCallable(() -> {
ArrayList<ApiResult> categoryNodes = null;
return Single.fromCallable((Callable<List<String>>) () -> {
ArrayList<ApiResult> categoryNodes;
try {
categoryNodes = api.action("query")
@ -287,7 +286,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
.getNodes("/api/query/search/p/@title");
} catch (IOException e) {
Timber.e("Failed to obtain searchTitles", e);
return new ArrayList();
return Collections.emptyList();
}
if (categoryNodes == null) {
@ -302,8 +301,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
}
return titleCategories;
})
.flatMapObservable(list -> Observable.fromIterable(list));
}).flatMapObservable(Observable::fromIterable);
}
@Override
@ -392,7 +390,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
public UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, final ProgressListener progressListener) throws IOException {
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
Log.e("WTF", "Result: "+result.toString());
Log.e("WTF", "Result: " +result.toString());
String resultStatus = result.getString("/api/upload/@result");
if (!resultStatus.equals("Success")) {

View file

@ -41,12 +41,12 @@ public class LogBuilder {
try {
fullData.put("schema", schema);
fullData.put("revision", rev);
fullData.put("wiki", CommonsApplication.EVENTLOG_WIKI);
fullData.put("wiki", BuildConfig.EVENTLOG_WIKI);
data.put("device", EventLog.DEVICE);
data.put("platform", "Android/" + Build.VERSION.RELEASE);
data.put("appversion", "Android/" + BuildConfig.VERSION_NAME);
fullData.put("event", data);
return new URL(CommonsApplication.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
return new URL(BuildConfig.EVENTLOG_URL + "?" + Utils.urlEncode(fullData.toString()) + ";");
} catch (MalformedURLException | JSONException e) {
throw new RuntimeException(e);
}

View file

@ -5,7 +5,6 @@ import android.support.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.Single;

View file

@ -10,7 +10,6 @@ import com.mapbox.mapboxsdk.annotations.IconFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

View file

@ -63,10 +63,10 @@ public class NearbyInfoDialog extends OverlayDialog {
overflowButton.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
overflowButton.setOnClickListener(this::popupMenuListener);
overflowButton.setOnClickListener(v -> popupMenuListener());
}
private void popupMenuListener(View v) {
private void popupMenuListener() {
PopupMenu popupMenu = new PopupMenu(getActivity(), overflowButton);
popupMenu.inflate(R.menu.nearby_info_dialog_options);

View file

@ -4,7 +4,6 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -13,7 +12,6 @@ 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.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
@ -22,7 +20,6 @@ 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;

View file

@ -1,13 +1,11 @@
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;

View file

@ -24,15 +24,13 @@ import fr.free.nrw.commons.nearby.NearbyActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
import timber.log.Timber;
public class NavigationBaseActivity extends BaseActivity
public abstract class NavigationBaseActivity extends BaseActivity
implements NavigationView.OnNavigationItemSelectedListener {
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.navigation_view)
NavigationView navigationView;
@BindView(R.id.drawer_layout)
DrawerLayout drawerLayout;
@ -121,16 +119,9 @@ public class NavigationBaseActivity extends BaseActivity
.setMessage(R.string.logout_verification)
.setCancelable(false)
.setPositiveButton(R.string.yes, (dialog, which) -> {
((CommonsApplication) getApplicationContext())
.clearApplicationData(NavigationBaseActivity.this, () -> {
Timber.d("Logout complete callback received.");
Intent nearbyIntent = new Intent(
NavigationBaseActivity.this, LoginActivity.class);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(nearbyIntent);
finish();
});
BaseLogoutListener logoutListener = new BaseLogoutListener();
CommonsApplication app = (CommonsApplication) getApplication();
app.clearApplicationData(this, logoutListener);
})
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
.show();
@ -140,7 +131,16 @@ public class NavigationBaseActivity extends BaseActivity
}
}
public interface LogoutListener {
void onLogoutComplete();
private class BaseLogoutListener implements CommonsApplication.LogoutListener {
@Override
public void onLogoutComplete() {
Timber.d("Logout complete callback received.");
Intent nearbyIntent = new Intent(
NavigationBaseActivity.this, LoginActivity.class);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(nearbyIntent);
finish();
}
}
}

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.upload;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;

View file

@ -63,7 +63,7 @@ public class MultipleShareActivity
@Override
public int getTotalMediaCount() {
if(photosList == null) {
if (photosList == null) {
return 0;
}
return photosList.size();
@ -71,7 +71,7 @@ public class MultipleShareActivity
@Override
public void notifyDatasetChanged() {
if(uploadsList != null) {
if (uploadsList != null) {
uploadsList.notifyDatasetChanged();
}
}
@ -145,7 +145,7 @@ public class MultipleShareActivity
uploadsList.setImageOnlyMode(true);
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
if(categorizationFragment == null) {
if (categorizationFragment == null) {
categorizationFragment = new CategorizationFragment();
}
// FIXME: Stops the keyboard from being shown 'stale' while moving out of this fragment into the next
@ -162,7 +162,7 @@ public class MultipleShareActivity
@Override
public void onCategoriesSave(List<String> categories) {
if(categories.size() > 0) {
if (categories.size() > 0) {
ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
for(Contribution contribution: photosList) {
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
@ -191,7 +191,7 @@ public class MultipleShareActivity
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case android.R.id.home:
if(mediaDetails.isVisible()) {
if (mediaDetails.isVisible()) {
getSupportFragmentManager().popBackStack();
}
return true;
@ -209,7 +209,7 @@ public class MultipleShareActivity
ButterKnife.bind(this);
initDrawer();
if(savedInstanceState != null) {
if (savedInstanceState != null) {
photosList = savedInstanceState.getParcelableArrayList("uploadsList");
}
@ -225,7 +225,7 @@ public class MultipleShareActivity
}
private void showDetail(int i) {
if(mediaDetails == null ||!mediaDetails.isVisible()) {
if (mediaDetails == null ||!mediaDetails.isVisible()) {
mediaDetails = new MediaDetailPagerFragment(true);
getSupportFragmentManager()
.beginTransaction()
@ -248,8 +248,8 @@ public class MultipleShareActivity
app.getMWApi().setAuthCookie(authCookie);
Intent intent = getIntent();
if(intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
if(photosList == null) {
if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
if (photosList == null) {
photosList = new ArrayList<>();
ArrayList<Uri> urisList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
for(int i=0; i < urisList.size(); i++) {
@ -265,7 +265,7 @@ public class MultipleShareActivity
}
uploadsList = (MultipleUploadListFragment) getSupportFragmentManager().findFragmentByTag("uploadsList");
if(uploadsList == null) {
if (uploadsList == null) {
uploadsList = new MultipleUploadListFragment();
getSupportFragmentManager()
.beginTransaction()
@ -287,7 +287,7 @@ public class MultipleShareActivity
@Override
public void onBackPressed() {
super.onBackPressed();
if(categorizationFragment != null && categorizationFragment.isVisible()) {
if (categorizationFragment != null && categorizationFragment.isVisible()) {
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
.param("username", app.getCurrentAccount().name)
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
@ -307,7 +307,7 @@ public class MultipleShareActivity
@Override
public void onBackStackChanged() {
if(mediaDetails != null && mediaDetails.isVisible()) {
if (mediaDetails != null && mediaDetails.isVisible()) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);

View file

@ -77,8 +77,8 @@ public class MultipleUploadListFragment extends Fragment {
public View getView(int i, View view, ViewGroup viewGroup) {
UploadHolderView holder;
if(view == null) {
view = getLayoutInflater(null).inflate(R.layout.layout_upload_item, null);
if (view == null) {
view = LayoutInflater.from(getContext()).inflate(R.layout.layout_upload_item, viewGroup, false);
holder = new UploadHolderView();
holder.image = (SimpleDraweeView) view.findViewById(R.id.uploadImage);
holder.title = (TextView) view.findViewById(R.id.uploadTitle);
@ -94,17 +94,17 @@ public class MultipleUploadListFragment extends Fragment {
.build());
view.setTag(holder);
} else {
holder = (UploadHolderView)view.getTag();
holder = (UploadHolderView) view.getTag();
}
Contribution up = (Contribution)this.getItem(i);
Contribution up = (Contribution) this.getItem(i);
if(holder.imageUri == null || !holder.imageUri.equals(up.getLocalUri())) {
if (holder.imageUri == null || !holder.imageUri.equals(up.getLocalUri())) {
holder.image.setImageURI(up.getLocalUri().toString());
holder.imageUri = up.getLocalUri();
}
if(!imageOnlyMode) {
if (!imageOnlyMode) {
holder.overlay.setVisibility(View.VISIBLE);
holder.title.setText(up.getFilename());
} else {
@ -134,21 +134,21 @@ public class MultipleUploadListFragment extends Fragment {
int screenHeight = screenMetrics.heightPixels;
int picWidth = Math.min((int) Math.sqrt(screenWidth * screenHeight / count), screenWidth);
picWidth = Math.min((int)(192 * screenMetrics.density), Math.max((int) (120 * screenMetrics.density), picWidth / 48 * 48));
int picHeight = Math.min(picWidth, (int)(192 * screenMetrics.density)); // Max Height is same as Contributions list
picWidth = Math.min((int) (192 * screenMetrics.density), Math.max((int) (120 * screenMetrics.density), picWidth / 48 * 48));
int picHeight = Math.min(picWidth, (int) (192 * screenMetrics.density)); // Max Height is same as Contributions list
return new Point(picWidth, picHeight);
}
public void notifyDatasetChanged() {
if(photosAdapter != null) {
if (photosAdapter != null) {
photosAdapter.notifyDataSetChanged();
}
}
public void setImageOnlyMode(boolean mode) {
imageOnlyMode = mode;
if(imageOnlyMode) {
if (imageOnlyMode) {
baseTitle.setVisibility(View.GONE);
} else {
baseTitle.setVisibility(View.VISIBLE);
@ -159,13 +159,13 @@ public class MultipleUploadListFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, null);
photosGrid = (GridView)view.findViewById(R.id.multipleShareBackground);
baseTitle = (EditText)view.findViewById(R.id.multipleBaseTitle);
View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, container, false);
photosGrid = (GridView) view.findViewById(R.id.multipleShareBackground);
baseTitle = (EditText) view.findViewById(R.id.multipleBaseTitle);
photosAdapter = new PhotoDisplayAdapter();
photosGrid.setAdapter(photosAdapter);
photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener)getActivity());
photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
photoSize = calculatePicDimension(detailProvider.getTotalMediaCount());
photosGrid.setColumnWidth(photoSize.x);
@ -188,7 +188,7 @@ public class MultipleUploadListFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
switch (item.getItemId()) {
case R.id.menu_upload_multiple:
multipleUploadInitiatedHandler.OnMultipleUploadInitiated();
return true;
@ -200,7 +200,7 @@ public class MultipleUploadListFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider)getActivity();
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider) getActivity();
multipleUploadInitiatedHandler = (OnMultipleUploadInitiatedHandler) getActivity();
setHasOptionsMenu(true);
@ -213,12 +213,12 @@ public class MultipleUploadListFragment extends Fragment {
@Override
public void onTextChanged(CharSequence charSequence, int i1, int i2, int i3) {
for(int i = 0; i < detailProvider.getTotalMediaCount(); i++) {
for (int i = 0; i < detailProvider.getTotalMediaCount(); i++) {
Contribution up = (Contribution) detailProvider.getMediaAtPosition(i);
Boolean isDirty = (Boolean)up.getTag("isDirty");
if(isDirty == null || !isDirty) {
if(!TextUtils.isEmpty(charSequence)) {
up.setFilename(charSequence.toString() + " - " + ((Integer)up.getTag("sequence") + 1));
Boolean isDirty = (Boolean) up.getTag("isDirty");
if (isDirty == null || !isDirty) {
if (!TextUtils.isEmpty(charSequence)) {
up.setFilename(charSequence.toString() + " - " + ((Integer) up.getTag("sequence") + 1));
} else {
up.setFilename("");
}

View file

@ -3,12 +3,14 @@ package fr.free.nrw.commons.upload;
import android.Manifest;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
@ -24,10 +26,10 @@ import android.widget.Toast;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.SimpleDraweeView;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -53,9 +55,9 @@ import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
* Activity for the title/desc screen after image is selected. Also starts processing image
* GPS coordinates or user location (if enabled in Settings) for category suggestions.
*/
public class ShareActivity
extends AuthenticatedActivity
implements SingleUploadFragment.OnUploadActionInitiated,
public class ShareActivity
extends AuthenticatedActivity
implements SingleUploadFragment.OnUploadActionInitiated,
OnCategoriesSaveHandler {
private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1;
@ -119,7 +121,7 @@ public class ShareActivity
// and permission is not obtained.
return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri)
&& (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED);
!= PackageManager.PERMISSION_GRANTED);
}
private void uploadBegins() {
@ -145,7 +147,7 @@ public class ShareActivity
}
private void showPostUpload() {
if(categorizationFragment == null) {
if (categorizationFragment == null) {
categorizationFragment = new CategorizationFragment();
}
getSupportFragmentManager().beginTransaction()
@ -155,7 +157,7 @@ public class ShareActivity
@Override
public void onCategoriesSave(List<String> categories) {
if(categories.size() > 0) {
if (categories.size() > 0) {
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
@ -181,7 +183,7 @@ public class ShareActivity
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if(contribution != null) {
if (contribution != null) {
outState.putParcelable("contribution", contribution);
}
}
@ -189,7 +191,7 @@ public class ShareActivity
@Override
public void onBackPressed() {
super.onBackPressed();
if(categorizationFragment != null && categorizationFragment.isVisible()) {
if (categorizationFragment != null && categorizationFragment.isVisible()) {
EventLog.schema(CommonsApplication.EVENT_CATEGORIZATION_ATTEMPT)
.param("username", app.getCurrentAccount().name)
.param("categories-count", categorizationFragment.getCurrentSelectedCount())
@ -228,7 +230,7 @@ public class ShareActivity
ButterKnife.bind(this);
initBack();
app = CommonsApplication.getInstance();
backgroundImageView = (SimpleDraweeView)findViewById(R.id.backgroundImage);
backgroundImageView = (SimpleDraweeView) findViewById(R.id.backgroundImage);
backgroundImageView.setHierarchy(GenericDraweeHierarchyBuilder
.newInstance(getResources())
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
@ -254,7 +256,7 @@ public class ShareActivity
backgroundImageView.setImageURI(mediaUri);
}
if (savedInstanceState != null) {
if (savedInstanceState != null) {
contribution = savedInstanceState.getParcelable("contribution");
}
@ -279,7 +281,7 @@ public class ShareActivity
if (useNewPermissions && (!storagePermitted || !locationPermitted)) {
if (!storagePermitted && !locationPermitted) {
String permissionRationales =
getResources().getString(R.string.storage_permission_rationale) + "\n"
getResources().getString(R.string.read_storage_permission_rationale) + "\n"
+ getResources().getString(R.string.location_permission_rationale);
snackbar = requestPermissionUsingSnackBar(
permissionRationales,
@ -292,7 +294,7 @@ public class ShareActivity
textView.setMaxLines(3);
} else if (!storagePermitted) {
requestPermissionUsingSnackBar(
getString(R.string.storage_permission_rationale),
getString(R.string.read_storage_permission_rationale),
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_PERM_ON_CREATE_STORAGE);
} else if (!locationPermitted) {
@ -307,7 +309,7 @@ public class ShareActivity
SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
if(shareView == null && categorizationFragment == null) {
if (shareView == null && categorizationFragment == null) {
shareView = new SingleUploadFragment();
getSupportFragmentManager()
.beginTransaction()
@ -417,12 +419,27 @@ public class ShareActivity
// in older devices getPath() may fail depending on the source URI
// creating and using a copy of the file seems to work instead.
// TODO: there might be a more proper solution than this
String copyPath = getApplicationContext().getCacheDir().getAbsolutePath()
+ "/" + new Date().getTime() + ".jpg";
String copyPath = null;
try {
ParcelFileDescriptor descriptor
= getContentResolver().openFileDescriptor(mediaUri, "r");
if (descriptor != null) {
SharedPreferences sharedPref = PreferenceManager
.getDefaultSharedPreferences(CommonsApplication.getInstance());
boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true);
if (useExtStorage) {
copyPath = Environment.getExternalStorageDirectory().toString()
+ "/CommonsApp/" + new Date().getTime() + ".jpg";
File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
newFile.mkdir();
FileUtils.copy(
descriptor.getFileDescriptor(),
copyPath);
Timber.d("Filepath (copied): %s", copyPath);
return copyPath;
}
copyPath = getApplicationContext().getCacheDir().getAbsolutePath()
+ "/" + new Date().getTime() + ".jpg";
FileUtils.copy(
descriptor.getFileDescriptor(),
copyPath);
@ -439,6 +456,7 @@ public class ShareActivity
/**
* Gets coordinates for category suggestions, either from EXIF data or user location
*
* @param gpsEnabled if true use GPS
*/
private void getFileMetadata(boolean gpsEnabled) {
@ -474,7 +492,7 @@ public class ShareActivity
* Then initiates the calls to MediaWiki API through an instance of MwVolleyApi.
*/
public void useImageCoords() {
if(decimalCoords != null) {
if (decimalCoords != null) {
Timber.d("Decimal coords of image: %s", decimalCoords);
// Only set cache for this point if image has coords
@ -508,8 +526,7 @@ public class ShareActivity
try {
imageObj.unregisterLocationManager();
Timber.d("Unregistered locationManager");
}
catch (NullPointerException e) {
} catch (NullPointerException e) {
Timber.d("locationManager does not exist, not unregistered");
}
}
@ -524,7 +541,7 @@ public class ShareActivity
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
if(categorizationFragment!=null && categorizationFragment.isVisible()) {
if (categorizationFragment != null && categorizationFragment.isVisible()) {
categorizationFragment.showBackButtonDialog();
} else {
onBackPressed();

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.upload;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
@ -39,13 +38,10 @@ import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.settings.Prefs;
import timber.log.Timber;
public class SingleUploadFragment extends Fragment {
private SharedPreferences prefs;
private String license;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
public interface OnUploadActionInitiated {
void uploadActionInitiated(String title, String description);
}
public class SingleUploadFragment extends Fragment {
@BindView(R.id.titleEdit) EditText titleEdit;
@BindView(R.id.descEdit) EditText descEdit;
@ -53,13 +49,15 @@ public class SingleUploadFragment extends Fragment {
@BindView(R.id.share_license_summary) TextView licenseSummaryView;
@BindView(R.id.licenseSpinner) Spinner licenseSpinner;
private SharedPreferences prefs;
private String license;
private OnUploadActionInitiated uploadActionInitiatedHandler;
private TitleTextWatcher textWatcher = new TitleTextWatcher();
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.activity_share, menu);
if(titleEdit != null) {
if (titleEdit != null) {
menu.findItem(R.id.menu_upload_single).setEnabled(titleEdit.getText().length() != 0);
}
}
@ -88,8 +86,9 @@ public class SingleUploadFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_single_upload, null);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_single_upload, container, false);
ButterKnife.bind(this, rootView);
@ -112,10 +111,10 @@ public class SingleUploadFragment extends Fragment {
Timber.d(license);
ArrayAdapter<String> adapter;
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme",false)) {
if (PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("theme", false)) {
// dark theme
adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, licenseItems);
}else {
} else {
// light theme
adapter = new ArrayAdapter<>(getActivity(), R.layout.light_simple_spinner_dropdown_item, licenseItems);
}
@ -146,26 +145,27 @@ public class SingleUploadFragment extends Fragment {
super.onDestroyView();
}
@OnItemSelected(R.id.licenseSpinner) void onLicenseSelected(AdapterView<?> parent, View view, int position, long id) {
@OnItemSelected(R.id.licenseSpinner)
void onLicenseSelected(AdapterView<?> parent, View view, int position, long id) {
String licenseName = parent.getItemAtPosition(position).toString();
// Set selected color to white because it should be readable on random images.
TextView selectedText = (TextView) licenseSpinner.getChildAt(0);
if (selectedText != null ) {
if (selectedText != null) {
selectedText.setTextColor(Color.WHITE);
selectedText.setBackgroundColor(Color.TRANSPARENT);
}
String license;
if(getString(R.string.license_name_cc0).equals(licenseName)) {
if (getString(R.string.license_name_cc0).equals(licenseName)) {
license = Prefs.Licenses.CC0;
} else if(getString(R.string.license_name_cc_by).equals(licenseName)) {
} else if (getString(R.string.license_name_cc_by).equals(licenseName)) {
license = Prefs.Licenses.CC_BY_3;
} else if(getString(R.string.license_name_cc_by_sa).equals(licenseName)) {
} else if (getString(R.string.license_name_cc_by_sa).equals(licenseName)) {
license = Prefs.Licenses.CC_BY_SA_3;
} else if(getString(R.string.license_name_cc_by_four).equals(licenseName)) {
} else if (getString(R.string.license_name_cc_by_four).equals(licenseName)) {
license = Prefs.Licenses.CC_BY_4;
} else if(getString(R.string.license_name_cc_by_sa_four).equals(licenseName)) {
} else if (getString(R.string.license_name_cc_by_sa_four).equals(licenseName)) {
license = Prefs.Licenses.CC_BY_SA_4;
} else {
throw new IllegalStateException("Unknown licenseName: " + licenseName);
@ -177,10 +177,9 @@ public class SingleUploadFragment extends Fragment {
editor.commit();
}
@OnTouch(R.id.share_license_summary) boolean showLicence(View view, MotionEvent motionEvent) {
if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
@OnTouch(R.id.share_license_summary)
boolean showLicence(View view, MotionEvent motionEvent) {
if (motionEvent.getActionMasked() == ACTION_DOWN) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse(Utils.licenseUrlFor(license)));
@ -191,7 +190,8 @@ public class SingleUploadFragment extends Fragment {
}
}
@OnClick(R.id.titleDescButton) void setTitleDescButton() {
@OnClick(R.id.titleDescButton)
void setTitleDescButton() {
//Retrieve last title and desc entered
SharedPreferences titleDesc = PreferenceManager.getDefaultSharedPreferences(getActivity());
String title = titleDesc.getString("Title", "");
@ -205,57 +205,41 @@ public class SingleUploadFragment extends Fragment {
/**
* Copied from https://stackoverflow.com/a/26269435/8065933
*/
@OnTouch
(R.id.titleEdit) boolean titleInfo(View view, MotionEvent motionEvent) {
@OnTouch(R.id.titleEdit)
boolean titleInfo(View view, MotionEvent motionEvent) {
//Should replace right with end to support different right-to-left languages as well
final int value = titleEdit.getRight() - titleEdit.getCompoundDrawables()[2].getBounds().width();
if (motionEvent.getAction() == motionEvent.ACTION_UP && motionEvent.getRawX() >= value) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(R.string.media_detail_title);
builder.setMessage(R.string.title_info);
builder.setCancelable(true);
builder.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.media_detail_title)
.setMessage(R.string.title_info)
.setCancelable(true)
.setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
.create()
.show();
return true;
}
return false;
}
@OnTouch
(R.id.descEdit) boolean descriptionInfo(View view, MotionEvent motionEvent) {
@OnTouch(R.id.descEdit)
boolean descriptionInfo(View view, MotionEvent motionEvent) {
final int value = descEdit.getRight() - descEdit.getCompoundDrawables()[2].getBounds().width();
if (motionEvent.getAction() == motionEvent.ACTION_UP && motionEvent.getRawX() >= value) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(R.string.media_detail_description);
builder.setMessage(R.string.description_info);
builder.setCancelable(true);
builder.setNeutralButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
return true;
if (motionEvent.getAction() == ACTION_UP && motionEvent.getRawX() >= value) {
new AlertDialog.Builder(getContext())
.setTitle(R.string.media_detail_description)
.setMessage(R.string.description_info)
.setCancelable(true)
.setNeutralButton(android.R.string.ok, (dialog, id) -> dialog.cancel())
.create()
.show();
return true;
}
return false;
}
private void setLicenseSummary(String license) {
licenseSummaryView.setText(getString(R.string.share_license_summary, getString(Utils.licenseNameFor(license))));
}
@ -279,16 +263,22 @@ public class SingleUploadFragment extends Fragment {
}
}
public interface OnUploadActionInitiated {
void uploadActionInitiated(String title, String description);
}
private class TitleTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
}
@Override
public void afterTextChanged(Editable editable) {
if(getActivity() != null) {
if (getActivity() != null) {
getActivity().invalidateOptionsMenu();
}
}

View file

@ -15,7 +15,7 @@ public class FileUtils {
* @return the content of the file
*/
public static String readFromResource(String fileName) throws IOException {
StringBuffer buffer = new StringBuffer();
StringBuilder buffer = new StringBuilder();
BufferedReader reader = null;
try {
reader = new BufferedReader(

View file

@ -0,0 +1,46 @@
package fr.free.nrw.commons.utils;
import java.util.Comparator;
import info.debatty.java.stringsimilarity.Levenshtein;
public class StringSortingUtils {
private StringSortingUtils() {
//no-op
}
/**
* Returns Comparator for sorting strings by its similarity with Levenshtein
* algorithm. By using this Comparator we get results from the highest to
* the lowest match.
*
* @param filter pattern to compare similarity
* @return Comparator with string similarity
*/
public static Comparator<String> sortBySimilarity(final String filter) {
return (firstItem, secondItem) -> {
double firstItemSimilarity = calculateSimilarity(firstItem, filter);
double secondItemSimilarity = calculateSimilarity(secondItem, filter);
return (int) Math.signum(secondItemSimilarity - firstItemSimilarity);
};
}
private static double calculateSimilarity(String firstString, String secondString) {
String longer = firstString.toLowerCase();
String shorter = secondString.toLowerCase();
if (firstString.length() < secondString.length()) {
longer = secondString;
shorter = firstString;
}
int longerLength = longer.length();
if (longerLength == 0) {
return 1.0;
}
double distanceBetweenStrings = new Levenshtein().distance(longer, shorter);
return (longerLength - distanceBetweenStrings) / (double) longerLength;
}
}

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#352200" android:pathData="M116.97,97.32c-38.84,0 -71.25,11.73 -78.86,27.34c0.63,7.06 2.16,13.86 4.51,20.28c12.15,12.41 40.86,21.13 74.35,21.13c35.18,0 65.08,-9.63 76.06,-23.04c1.76,-5.21 3,-10.67 3.65,-16.3C191.03,110.1 157.5,97.32 116.97,97.32z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#007F9F" android:pathData="M177.13,170.39c2.87,-3.23 5.47,-6.69 7.79,-10.36c-8.9,-5.09 -35.72,-8.79 -67.42,-8.79s-58.52,3.7 -67.42,8.79c2.32,3.66 4.92,7.13 7.79,10.36c12.51,3.49 34.53,5.81 59.63,5.81C142.6,176.2 164.63,173.88 177.13,170.39z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#FF000000" android:pathData="M66.97,166.68a50.53,7.7 0,1 0,101.06 0a50.53,7.7 0,1 0,-101.06 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.001" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48s85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M117.5,117.5m-79.73,0a79.73,79.73 0,1 1,159.45 0a79.73,79.73 0,1 1,-159.45 0"/>
<path android:fillColor="#FF000000" android:pathData="M49.31,154.69a68.19,8.86 0,1 0,136.39 0a68.19,8.86 0,1 0,-136.39 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="112.8"
android:viewportWidth="112.8" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h112.8v112.8h-112.8z"/>
<path android:fillColor="#E3E3E3" android:pathData="M56.4,8.88c-26.24,0 -47.52,21.28 -47.52,47.52c0,26.24 21.27,47.52 47.52,47.52c26.25,0 47.52,-21.28 47.52,-47.52C103.92,30.16 82.64,8.88 56.4,8.88zM56.4,97.43c-22.66,0 -41.03,-18.37 -41.03,-41.03c0,-22.66 18.37,-41.03 41.03,-41.03c22.66,0 41.03,18.37 41.03,41.03C97.43,79.06 79.06,97.43 56.4,97.43z"/>
<path android:fillColor="#54B948" android:pathData="M56.4,56.73m-38.27,0a38.27,38.27 0,1 1,76.54 0a38.27,38.27 0,1 1,-76.54 0"/>
<path android:fillColor="#352200" android:pathData="M28.6,69.79a27.8,10.31 0,1 0,55.6 0a27.8,10.31 0,1 0,-55.6 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#FF000000" android:pathData="M56.94,153.5a60.56,8.91 0,1 0,121.12 0a60.56,8.91 0,1 0,-121.12 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.001" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48s85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M117.5,117.5m-79.73,0a79.73,79.73 0,1 1,159.45 0a79.73,79.73 0,1 1,-159.45 0"/>
<path android:fillColor="#FF000000" android:pathData="M73.44,165.32a44.06,7.71 0,1 0,88.13 0a44.06,7.71 0,1 0,-88.13 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#FF000000" android:pathData="M64.19,148.92a53.31,8.12 0,1 0,106.62 0a53.31,8.12 0,1 0,-106.62 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="112.801"
android:viewportWidth="112.8" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h112.8v112.8h-112.8z"/>
<path android:fillColor="#E3E3E3" android:pathData="M56.4,8.88c-26.24,0 -47.52,21.28 -47.52,47.52s21.27,47.52 47.52,47.52c26.25,0 47.52,-21.28 47.52,-47.52S82.64,8.88 56.4,8.88zM56.4,97.43c-22.66,0 -41.03,-18.37 -41.03,-41.03c0,-22.66 18.37,-41.03 41.03,-41.03c22.66,0 41.03,18.37 41.03,41.03C97.43,79.06 79.06,97.43 56.4,97.43z"/>
<path android:fillColor="#54B948" android:pathData="M56.4,56.73m-38.27,0a38.27,38.27 0,1 1,76.54 0a38.27,38.27 0,1 1,-76.54 0"/>
<path android:fillColor="#007F9F" android:pathData="M18.13,56.73c0,21.13 17.13,38.27 38.27,38.27c21.14,0 38.27,-17.13 38.27,-38.27c0,-4.1 -0.65,-8.03 -1.84,-11.73H19.97C18.78,48.7 18.13,52.64 18.13,56.73z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48s85.48,38.27 85.48,85.48S164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#FF000000" android:pathData="M73.27,163.84a44.23,6.61 0,1 0,88.46 0a44.23,6.61 0,1 0,-88.46 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.68,0 99,-44.33 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#54B948" android:pathData="M117.5,118.19m-79.73,0a79.73,79.73 0,1 1,159.45 0a79.73,79.73 0,1 1,-159.45 0"/>
<path android:fillColor="#FF000000" android:pathData="M54.44,151.1a62.06,8.91 0,1 0,124.12 0a62.06,8.91 0,1 0,-124.12 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="112.8"
android:viewportWidth="112.8" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h112.8v112.8h-112.8z"/>
<path android:fillColor="#E3E3E3" android:pathData="M56.4,8.88c-26.24,0 -47.52,21.27 -47.52,47.52c0,26.24 21.27,47.52 47.52,47.52c26.25,0 47.52,-21.28 47.52,-47.52C103.92,30.16 82.64,8.88 56.4,8.88zM56.4,97.43c-22.66,0 -41.03,-18.37 -41.03,-41.03s18.37,-41.03 41.03,-41.03c22.66,0 41.03,18.37 41.03,41.03S79.06,97.43 56.4,97.43z"/>
<path android:fillColor="#54B948" android:pathData="M56.4,56.73m-38.27,0a38.27,38.27 0,1 1,76.54 0a38.27,38.27 0,1 1,-76.54 0"/>
<path android:fillColor="#007F9F" android:pathData="M18.13,56.73c0,21.14 17.13,38.27 38.27,38.27c21.14,0 38.27,-17.13 38.27,-38.27c0,-4.09 -0.65,-8.03 -1.84,-11.73H19.97C18.78,48.7 18.13,52.64 18.13,56.73z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#54B948" android:pathData="M50.99,153.38a65.64,7.45 0,1 0,131.28 0a65.64,7.45 0,1 0,-131.28 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#352200" android:pathData="M48.69,144.99a69.93,13.72 0,1 0,139.86 0a69.93,13.72 0,1 0,-139.86 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="112.801"
android:viewportWidth="112.801" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h112.8v112.8h-112.8z"/>
<path android:fillColor="#E3E3E3" android:pathData="M56.4,8.88c-26.24,0 -47.52,21.27 -47.52,47.52c0,26.24 21.27,47.52 47.52,47.52c26.25,0 47.52,-21.27 47.52,-47.52C103.92,30.16 82.64,8.88 56.4,8.88zM56.4,97.43c-22.66,0 -41.03,-18.37 -41.03,-41.03c0,-22.66 18.37,-41.03 41.03,-41.03c22.66,0 41.03,18.37 41.03,41.03C97.43,79.06 79.06,97.43 56.4,97.43z"/>
<path android:fillColor="#54B948" android:pathData="M56.4,56.4m-38.27,0a38.27,38.27 0,1 1,76.54 0a38.27,38.27 0,1 1,-76.54 0"/>
<path android:fillColor="#352200" android:pathData="M19.31,61.45a36.97,17.44 0,1 0,73.93 0a36.97,17.44 0,1 0,-73.93 0z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="234.999"
android:viewportWidth="235.001" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48s85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#352200" android:pathData="M195.22,99.74c-4.62,-1.5 -10.24,-2.97 -14.99,-3.58l-37.11,-5.24c-42.73,-5.28 -76.6,-2.88 -101.46,1.97c-2.52,7.75 -3.89,16.02 -3.89,24.61c0,44.03 35.69,79.72 79.73,79.72c44.03,0 79.73,-35.69 79.73,-79.72C197.23,111.39 196.52,105.46 195.22,99.74z"/>

View file

@ -1,6 +1,5 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.68,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.67,0 99,-44.32 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#EDA33F" android:pathData="M197.23,117.5c0,44.03 -35.69,79.72 -79.73,79.72c-44.03,0 -79.73,-35.69 -79.73,-79.72c0,-44.03 35.69,-79.73 79.73,-79.73C161.53,37.77 197.23,73.47 197.23,117.5z"/>
<path android:fillColor="#FF000000" android:pathData="M51.77,153.81a65.73,6.07 0,1 0,131.47 0a65.73,6.07 0,1 0,-131.47 0z"/>

View file

@ -1,18 +1,17 @@
<vector android:height="24dp" android:viewportHeight="235.0"
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#B1B1B1" android:pathData="M0,0h235v235h-235z"/>
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.68,0 99,-44.33 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#54B948" android:pathData="M117.5,118.19m-79.73,0a79.73,79.73 0,1 1,159.45 0a79.73,79.73 0,1 1,-159.45 0"/>
<path android:fillColor="#58595B" android:pathData="M117.5,197.91c44.03,0 79.73,-35.69 79.73,-79.72c0,-7.58 -1.08,-14.9 -3.06,-21.85c-27.77,-4.05 -53.39,-3.37 -55.11,0.61H91.73c-3.07,-5.81 -25.77,-5.67 -49.91,-3.83c-2.61,7.89 -4.04,16.31 -4.04,25.07C37.77,162.22 73.47,197.91 117.5,197.91z"/>
<path android:fillColor="#3D3D3E" android:pathData="M176.06,147.04c-5,-10.15 -18.33,-10.73 -20.33,-16.44c-1.53,-4.37 3.93,-20.85 0.29,-36.94c-9.63,0.21 -16.12,1.36 -16.96,3.3H91.73c-1.53,-2.89 -7.93,-4.31 -16.94,-4.78c3.91,15.01 8.94,33.28 11.94,39.63c3.67,7.75 2,17.74 2.33,27.58c0.22,6.6 -0.97,20.34 -1,32.89c9.11,3.62 19.03,5.64 29.44,5.64c24.39,0 46.21,-10.96 60.83,-28.22C178.44,159.86 178.05,151.07 176.06,147.04z"/>
<path android:fillColor="#007F9F" android:pathData="M117.5,197.91c16.31,0 31.46,-4.9 44.09,-13.3c-0.56,-5.74 -1.6,-13.6 -3.53,-22.06c-3.83,-16.83 -13.67,-24.67 -14.5,-28.83s5.5,-30.04 -4.5,-36.77c0,0 -18.33,-1.77 -47.33,0c2.83,7.27 5.83,21.44 8.5,27.1c2.67,5.67 2.17,17.83 3,24.33c0.73,5.67 2.31,34.44 0.22,48.27C108.01,197.46 112.7,197.91 117.5,197.91z"/>
<path android:fillColor="#FFFFFF" android:pathData="M161.59,184.61c-0.56,-5.74 -1.6,-13.6 -3.53,-22.06c-3.83,-16.83 -13.67,-24.67 -14.5,-28.83s5.5,-30.04 -4.5,-36.77c0,0 -0.43,-0.04 -1.26,-0.1c1.76,6.13 6.76,10.69 4.76,20.66s-2.25,18.22 2.25,25.47c3.95,6.37 15.43,26.47 14.69,42.97C160.21,185.51 160.9,185.07 161.59,184.61z"/>
<path android:fillColor="#FFFFFF" android:pathData="M103.23,148.39c0.73,5.67 2.31,34.44 0.22,48.27c2.45,0.44 4.94,0.76 7.46,0.97c-3.13,-11.2 -4.51,-35.25 -4.1,-42.4c0.5,-8.75 -1.75,-19 -3.25,-28c-1.35,-8.11 -5.75,-20.49 -10.33,-30.36c-0.5,0.03 -1,0.06 -1.5,0.09c2.83,7.27 5.83,21.44 8.5,27.1C102.89,129.72 102.39,141.89 103.23,148.39z"/>
<path android:fillColor="#FFFFFF" android:pathData="M102.56,98.97c2.88,4.13 8.88,14.75 9.13,29.5C114.31,119.22 114.69,107.1 102.56,98.97z"/>
<path android:fillColor="#FFFFFF" android:pathData="M115.06,101.1c2.5,4.13 7.73,14.75 7.94,29.5C125.29,121.35 125.61,109.22 115.06,101.1z"/>
<path android:fillColor="#FFFFFF" android:pathData="M125.64,100.1c2.12,3.5 6.55,12.5 6.73,25C134.31,117.26 134.59,106.98 125.64,100.1z"/>
<path android:fillColor="#FFFFFF" android:pathData="M111.03,134.72c0.31,7.5 5.88,9.38 9.16,22.75s-1.9,16.5 0,25.13c-3.47,-10.13 -0.47,-17.25 -3.35,-25S110.6,140.97 111.03,134.72z"/>
<path android:fillColor="#FFFFFF" android:pathData="M123.85,144.12c0.23,6.87 4.35,8.58 6.77,20.83c2.42,12.24 -1.4,15.1 0,23c-2.57,-9.27 -0.35,-15.79 -2.47,-22.89S123.52,149.84 123.85,144.12z"/>
<path android:fillColor="#FFFFFF" android:pathData="M129.24,131.8c0.53,6.85 4.72,8.38 7.68,20.51c2.96,12.13 -0.74,15.15 1.02,22.98c-2.97,-9.15 -1.05,-15.76 -3.48,-22.75S129.18,137.54 129.24,131.8z"/>
<path android:fillColor="#FFFFFF" android:pathData="M139.08,138.81c0.55,6.85 4.74,8.37 7.72,20.49c2.98,12.12 -0.7,15.15 1.06,22.98c-2.99,-9.14 -1.08,-15.76 -3.53,-22.75C141.88,152.54 139.02,144.54 139.08,138.81z"/>
android:viewportWidth="235.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#E3E3E3" android:pathData="M117.5,18.5c-54.67,0 -99,44.32 -99,99c0,54.67 44.32,99 99,99c54.68,0 99,-44.33 99,-99C216.5,62.83 172.18,18.5 117.5,18.5zM117.5,202.98c-47.21,0 -85.48,-38.27 -85.48,-85.48c0,-47.21 38.27,-85.48 85.48,-85.48c47.21,0 85.48,38.27 85.48,85.48C202.98,164.71 164.71,202.98 117.5,202.98z"/>
<path android:fillColor="#54B948" android:pathData="M117.5,118.19m-79.73,0a79.73,79.73 0,1 1,159.45 0a79.73,79.73 0,1 1,-159.45 0"/>
<path android:fillColor="#58595B" android:pathData="M117.5,197.91c44.03,0 79.73,-35.69 79.73,-79.72c0,-7.58 -1.08,-14.9 -3.06,-21.85c-27.77,-4.05 -53.39,-3.37 -55.11,0.61H91.73c-3.07,-5.81 -25.77,-5.67 -49.91,-3.83c-2.61,7.89 -4.04,16.31 -4.04,25.07C37.77,162.22 73.47,197.91 117.5,197.91z"/>
<path android:fillColor="#3D3D3E" android:pathData="M176.06,147.04c-5,-10.15 -18.33,-10.73 -20.33,-16.44c-1.53,-4.37 3.93,-20.85 0.29,-36.94c-9.63,0.21 -16.12,1.36 -16.96,3.3H91.73c-1.53,-2.89 -7.93,-4.31 -16.94,-4.78c3.91,15.01 8.94,33.28 11.94,39.63c3.67,7.75 2,17.74 2.33,27.58c0.22,6.6 -0.97,20.34 -1,32.89c9.11,3.62 19.03,5.64 29.44,5.64c24.39,0 46.21,-10.96 60.83,-28.22C178.44,159.86 178.05,151.07 176.06,147.04z"/>
<path android:fillColor="#007F9F" android:pathData="M117.5,197.91c16.31,0 31.46,-4.9 44.09,-13.3c-0.56,-5.74 -1.6,-13.6 -3.53,-22.06c-3.83,-16.83 -13.67,-24.67 -14.5,-28.83s5.5,-30.04 -4.5,-36.77c0,0 -18.33,-1.77 -47.33,0c2.83,7.27 5.83,21.44 8.5,27.1c2.67,5.67 2.17,17.83 3,24.33c0.73,5.67 2.31,34.44 0.22,48.27C108.01,197.46 112.7,197.91 117.5,197.91z"/>
<path android:fillColor="#FFFFFF" android:pathData="M161.59,184.61c-0.56,-5.74 -1.6,-13.6 -3.53,-22.06c-3.83,-16.83 -13.67,-24.67 -14.5,-28.83s5.5,-30.04 -4.5,-36.77c0,0 -0.43,-0.04 -1.26,-0.1c1.76,6.13 6.76,10.69 4.76,20.66s-2.25,18.22 2.25,25.47c3.95,6.37 15.43,26.47 14.69,42.97C160.21,185.51 160.9,185.07 161.59,184.61z"/>
<path android:fillColor="#FFFFFF" android:pathData="M103.23,148.39c0.73,5.67 2.31,34.44 0.22,48.27c2.45,0.44 4.94,0.76 7.46,0.97c-3.13,-11.2 -4.51,-35.25 -4.1,-42.4c0.5,-8.75 -1.75,-19 -3.25,-28c-1.35,-8.11 -5.75,-20.49 -10.33,-30.36c-0.5,0.03 -1,0.06 -1.5,0.09c2.83,7.27 5.83,21.44 8.5,27.1C102.89,129.72 102.39,141.89 103.23,148.39z"/>
<path android:fillColor="#FFFFFF" android:pathData="M102.56,98.97c2.88,4.13 8.88,14.75 9.13,29.5C114.31,119.22 114.69,107.1 102.56,98.97z"/>
<path android:fillColor="#FFFFFF" android:pathData="M115.06,101.1c2.5,4.13 7.73,14.75 7.94,29.5C125.29,121.35 125.61,109.22 115.06,101.1z"/>
<path android:fillColor="#FFFFFF" android:pathData="M125.64,100.1c2.12,3.5 6.55,12.5 6.73,25C134.31,117.26 134.59,106.98 125.64,100.1z"/>
<path android:fillColor="#FFFFFF" android:pathData="M111.03,134.72c0.31,7.5 5.88,9.38 9.16,22.75s-1.9,16.5 0,25.13c-3.47,-10.13 -0.47,-17.25 -3.35,-25S110.6,140.97 111.03,134.72z"/>
<path android:fillColor="#FFFFFF" android:pathData="M123.85,144.12c0.23,6.87 4.35,8.58 6.77,20.83c2.42,12.24 -1.4,15.1 0,23c-2.57,-9.27 -0.35,-15.79 -2.47,-22.89S123.52,149.84 123.85,144.12z"/>
<path android:fillColor="#FFFFFF" android:pathData="M129.24,131.8c0.53,6.85 4.72,8.38 7.68,20.51c2.96,12.13 -0.74,15.15 1.02,22.98c-2.97,-9.15 -1.05,-15.76 -3.48,-22.75S129.18,137.54 129.24,131.8z"/>
<path android:fillColor="#FFFFFF" android:pathData="M139.08,138.81c0.55,6.85 4.74,8.37 7.72,20.49c2.98,12.12 -0.7,15.15 1.06,22.98c-2.99,-9.14 -1.08,-15.76 -3.53,-22.75C141.88,152.54 139.02,144.54 139.08,138.81z"/>
</vector>

View file

@ -1,66 +1,68 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:foreground="?attr/selectableItemBackground"
android:layout_height="@dimen/icon_size"
android:layout_height="wrap_content"
android:foreground="?selectableItemBackground"
android:minHeight="72dp"
>
<RelativeLayout
<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:background="@android:color/white"
android:contentDescription="@string/no_image_found"
android:scaleType="centerCrop"
android:src="@drawable/empty_photo"
/>
<TextView
android:id="@+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="@string/placeholder_place_distance"
/>
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
android:layout_height="wrap_content"
android:layout_alignTop="@id/distance"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_toEndOf="@id/icon"
android:layout_toLeftOf="@id/distance"
android:layout_toRightOf="@id/icon"
android:layout_toStartOf="@id/distance"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
tools:text="@string/placeholder_place_name"
/>
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/icon_size"
android:layout_height="match_parent"
android:src="@drawable/empty_photo"
android:background="#ffffff"
android:scaleType="centerCrop"
android:contentDescription="@string/no_image_found"
/>
<TextView
android:id="@+id/tvDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/distance"
android:layout_alignLeft="@id/tvName"
android:layout_alignRight="@id/distance"
android:layout_alignStart="@id/tvName"
android:layout_below="@id/tvName"
android:layout_marginBottom="16dp"
android:ellipsize="end"
android:maxLines="4"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
tools:text="@string/placeholder_place_description"
/>
<TextView
android:id="@+id/distance"
android:layout_width="@dimen/icon_size"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/icon"
android:gravity="center"
android:padding="@dimen/tiny_margin"
style="?android:textAppearanceSmallInverse"
android:textColor="#ffffff"
android:background="@color/text_background"
tools:text="@string/placeholder_place_distance"
/>
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/icon"
android:layout_toRightOf="@+id/icon"
android:paddingTop="@dimen/small_margin"
android:paddingLeft="@dimen/small_margin"
android:paddingRight="@dimen/small_margin"
android:maxLines="2"
android:ellipsize="none"
style="?android:textAppearanceMedium"
tools:text="@string/placeholder_place_name"
/>
<TextView
android:id="@+id/tvDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/icon"
android:layout_toRightOf="@+id/icon"
android:layout_below="@+id/tvName"
android:padding="@dimen/small_margin"
android:ellipsize="none"
android:maxLines="4"
style="?android:textAppearanceSmall"
tools:text="@string/placeholder_place_description"
/>
</RelativeLayout>
</FrameLayout>
</RelativeLayout>

View file

@ -47,6 +47,8 @@
<string name="categories_search_text_hint">تصنيفات البحث</string>
<string name="menu_save_categories">احفظ</string>
<string name="refresh_button">أنعش</string>
<string name="enable_gps">تفعيل GPS</string>
<string name="contributions_subtitle_zero">لا مرفوعات بعد</string>
<string name="categories_not_found">لا توجد تصنيفات تطابق %1$s</string>
<string name="categories_skip_explanation">أضف التصانيف لتسهل اكتشاف صورك على ويكيميديا كومنز.\n\nابدأ الكتاب لتضيف التصانيف.\nانقر هذه الرسالة لتتجاوز هذه الخطوة.</string>
<string name="categories_activity_title">تصنيفات</string>
@ -81,10 +83,22 @@
<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="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="media_detail_license">الترخيص</string>
<string name="media_detail_coordinates">الإحداثيات</string>
<string name="use_wikidata">استخدم ويكي بيانات</string>
<string name="maximum_limit">الحد الأقصى</string>
<string name="navigation_item_home">الرئيسية</string>
<string name="navigation_item_upload">رفع</string>
<string name="navigation_item_nearby">بالقرب من هنا</string>
<string name="navigation_item_about">حول</string>
<string name="navigation_item_settings">الإعدادات</string>
<string name="navigation_item_logout">تسجيل الخروج</string>
<string name="nearby_info_menu_commons_article">صفحة ملف كومنز</string>
</resources>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Ensin descripción</string>
<string name="detail_license_empty">Llicencia desconocida</string>
<string name="menu_refresh">Refrescar</string>
<string name="storage_permission_rationale">Permisu riquíu: llectura d\'almacenamientu esternu. L\'aplicación nun puede funcionar ensin él.</string>
<string name="read_storage_permission_rationale">Permisu riquíu: llectura d\'almacenamientu esternu. L\'aplicación nun puede funcionar ensin él.</string>
<string name="write_storage_permission_rationale">Permisu riquíu: escritura d\'almacenamientu esternu. L\'aplicación nun puede funcionar ensin él.</string>
<string name="location_permission_rationale">Permisu opcional: llograr l\'allugamientu actual pa suxerir categoríes</string>
<string name="ok">Aceutar</string>
<string name="title_activity_nearby">Llugares cercanos</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Error al poner les fotos na caché</string>
<string name="title_info">Un títulu descriptivu únicu pal ficheru, que sirvirá para da-y nome al ficheru. Se pue usar llinguax normal con espacios. Nun amiestes la estensión del ficheru</string>
<string name="description_info">Por favor, describi l\'elementu multimedia tantu como sía posible: ¿ónde se tomó?, ¿qué amuesa?, ¿cuál ye\'l contestu? Por favor, describi los oxetos o persones. Revela la información que nun pueda aldovinase de mou cenciellu, por casu el momentu del día si ye un paisaxe. Si\'l mediu amuesa daqué desacostumao, esplica qué lo fai raro.</string>
<string name="use_external_storage">Usar almacenamientu esternu</string>
<string name="use_external_storage_summary">Guardar nel preséu les imaxes tomaes cola cámara de la app</string>
</resources>

View file

@ -85,6 +85,7 @@
<string name="menu_retry_upload">পুনঃচেষ্টা করুন</string>
<string name="menu_cancel_upload">বাতিল</string>
<string name="share_license_summary">এই ছবিটি %1$s এর অধীনে লাইসেন্স করা হবে</string>
<string name="media_upload_policy">এই চিত্র জমা দেয়ার দ্বারা, আমি ঘোষণা দিচ্ছি যে এটি আমার নিজস্ব কাজ, এতে কপিরাইটযুক্ত উপাদান নেই বা এটই সেলফি নয়, এবং &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines/bn\"&gt;উইকিমিডিয়া কমন্স নীতি&lt;/a&gt; অনুসরণ করে।</string>
<string name="menu_download">ডাউনলোড</string>
<string name="preference_license">লাইসেন্স</string>
<string name="use_previous">পূর্ববর্তী শিরোনাম/বিবরণ ব্যবহার করুন</string>
@ -132,7 +133,7 @@
<string name="detail_description_empty">বিবরণ নেই</string>
<string name="detail_license_empty">অজানা লাইসেন্স</string>
<string name="menu_refresh">পুনঃসতেজ</string>
<string name="storage_permission_rationale">প্রয়োজনীয় অনুমতি: বহিঃস্ত সঞ্চয়স্থান পড়া। এটি ছাড়া অ্যাপ কাজ করবে না।</string>
<string name="read_storage_permission_rationale">প্রয়োজনীয় অনুমতি: বহিঃস্ত সঞ্চয়স্থান পড়া। এটি ছাড়া অ্যাপ কাজ করবে না।</string>
<string name="location_permission_rationale">ঐচ্ছিক অনুমতি: বিষয়শ্রেণী পরামর্শের জন্য বর্তমান অবস্থান নেয়</string>
<string name="ok">ঠিক আছে</string>
<string name="title_activity_nearby">কাছাকাছি স্থান</string>
@ -148,6 +149,7 @@
<string name="media_detail_uploaded_date">আপলোডের তারিখ</string>
<string name="media_detail_license">লাইসেন্স</string>
<string name="media_detail_coordinates">স্থানাঙ্কসমূহ</string>
<string name="media_detail_coordinates_empty">কিছু দেয়া হয়নি</string>
<string name="become_a_tester_title">বিটা টেস্টার হোন</string>
<string name="use_wikidata">উইকিউপাত্ত ব্যবহার করুন</string>
<string name="use_wikidata_summary">(সতর্কতা: এটি নিষ্ক্রিয় করা অধিক পরিমাণে মোবাইল ডেটা খরচ হওয়ার কারণ হতে পারে)</string>
@ -166,6 +168,7 @@
<string name="welcome_image_rainbow_bridge">রংধনুর সেতু</string>
<string name="welcome_image_tulip">টিউলিপ</string>
<string name="welcome_image_no_selfies">কোন সেলফি নয়</string>
<string name="welcome_image_proprietary">মালিকানা চিত্র</string>
<string name="welcome_image_welcome_wikipedia">উইকিপিডিয়ায় স্বাগতম</string>
<string name="welcome_image_welcome_copyright">স্বাগতম কপিরাইট</string>
<string name="welcome_image_sydney_opera_house">সিডনি অপেরা হাউস</string>
@ -184,4 +187,5 @@
<string name="no_description_found">কোন বিবরণ পাওয়া যায়নি</string>
<string name="nearby_info_menu_commons_article">কমন্সে ফাইলের পাতা</string>
<string name="nearby_info_menu_wikidata_article">উইকিউপাত্ত পদ</string>
<string name="use_external_storage">বাহ্যিক সঞ্চয়স্থান ব্যবহার করুন</string>
</resources>

View file

@ -133,7 +133,7 @@
<string name="detail_description_empty">Deskrivadur ebet</string>
<string name="detail_license_empty">Aotre-implijout dizanv</string>
<string name="menu_refresh">Freskaat</string>
<string name="storage_permission_rationale">Aotre rekis : lenn ur stokañ diavaez. Hep se, n\'hall ket an arload mont en-dro.</string>
<string name="read_storage_permission_rationale">Aotre rekis : lenn ur stokañ diavaez. Hep se, n\'hall ket an arload mont en-dro.</string>
<string name="location_permission_rationale">Aotre diret : kaout al lec\'hiadur red evit kinnig rummadoù</string>
<string name="ok">Mat eo</string>
<string name="title_activity_nearby">Lec\'hioù nes</string>
@ -147,6 +147,9 @@
<string name="media_detail_description">Deskrivadur</string>
<string name="media_detail_description_explanation">Amañ e lakaer titl ar media. Gallout a ra bezañ hir-mat ha mont dre meur a linenn. Spi hon eus e vo bravik an disoc\'h koulskoude.</string>
<string name="media_detail_uploaded_date">Deiziad enporzhiañ</string>
<string name="media_detail_license">Aotre-implijout</string>
<string name="media_detail_coordinates">Daveennoù</string>
<string name="media_detail_coordinates_empty">N\'eo ket bet pourchaset</string>
<string name="become_a_tester_title">Deuit da vezañ un amprouer beta</string>
<string name="become_a_tester_description">En em enskrivañ en hor c\'hanol beta war Google Play ha kaout ur rakmoned d\'an arc\'hwelioù nevez d\'an drein difaziet</string>
<string name="use_wikidata">Ober gant Wikidata</string>

View file

@ -133,7 +133,7 @@
<string name="detail_description_empty">Nema opisa</string>
<string name="detail_license_empty">Nepoznata licenca</string>
<string name="menu_refresh">Osvježi</string>
<string name="storage_permission_rationale">Potrebna dozvola: Čitanje vanjske memorije. Aplikacija ne može raditi bez ovog.</string>
<string name="read_storage_permission_rationale">Potrebna dozvola: Čitanje vanjske memorije. Aplikacija ne može raditi bez ovog.</string>
<string name="location_permission_rationale">Neobavezna dozvola: Dobavljanje trenutne lokacije za predlaganje kategorija</string>
<string name="ok">U redu</string>
<string name="title_activity_nearby">Mjesta u blizini</string>

View file

@ -133,7 +133,7 @@
<string name="detail_description_empty">Bez popisu</string>
<string name="detail_license_empty">Neznámá licence</string>
<string name="menu_refresh">Obnovit</string>
<string name="storage_permission_rationale">Požadováno oprávnění ke čtení externího úložiště. Aplikace bez toho nemůže pracovat.</string>
<string name="read_storage_permission_rationale">Požadováno oprávnění ke čtení externího úložiště. Aplikace bez toho nemůže pracovat.</string>
<string name="location_permission_rationale">Volitelně: Umožněte aplikaci, aby získávala aktuální polohu a nabízela na jejím základě kategorie</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Místa v okolí</string>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Ingen beskrivelse</string>
<string name="detail_license_empty">Ukendt licens</string>
<string name="menu_refresh">Opdater</string>
<string name="storage_permission_rationale">Krævet tilladelse: Læs eksternt lager. Programmet kan ikke fungere uden denne tilladelse.</string>
<string name="read_storage_permission_rationale">Krævet tilladelse: Læs eksternt lager. Programmet kan ikke fungere uden denne tilladelse.</string>
<string name="write_storage_permission_rationale">Krævet tilladelse: Skriv til eksternt lager. Program kan ikke fungere uden denne funktion.</string>
<string name="location_permission_rationale">Valgfri tilladelse: Hent nuværende position for kategoriforslag</string>
<string name="ok">O.k.</string>
<string name="title_activity_nearby">Steder i nærheden</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Fejl under mellemlagring af billeder</string>
<string name="title_info">En unik beskrivelse for filen, som vil fungere som et filnavn. Du kan bruge normalt sprog med mellemrum. Udelad filendelsen.</string>
<string name="description_info">Beskriv mediet så godt som muligt: Hvor blev det taget? Hvad viser det? Hvad er konteksten? Beskriv objekterne eller personerne. Giv information som ikke nemt kan gættes, for eksempel hvornår på dagen billedet blev taget, om det er et landskabsbillede. Om billedet viser noget usædvanligt, forklar hvad som gør det usædvanlig.</string>
<string name="use_external_storage">Brug eksternt lager</string>
<string name="use_external_storage_summary">Gem billeder taget med din enheds program på kameraet</string>
</resources>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Keine Beschreibung</string>
<string name="detail_license_empty">Unbekannte Lizenz</string>
<string name="menu_refresh">Aktualisieren</string>
<string name="storage_permission_rationale">Erforderliche Berechtigung: Externen Speicher lesen. Die App funktioniert ohne diese Berechtigung nicht.</string>
<string name="read_storage_permission_rationale">Erforderliche Berechtigung: Externen Speicher lesen. Die App funktioniert ohne diese Berechtigung nicht.</string>
<string name="write_storage_permission_rationale">Erforderliche Berechtigung: Externen Speicher beschreiben. Die App kann ohne dies nicht funktionieren.</string>
<string name="location_permission_rationale">Optionale Berechtigung: Ruft den aktuellen Standort für Kategorievorschläge ab</string>
<string name="ok">Okay</string>
<string name="title_activity_nearby">Orte in der Nähe</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Fehler beim Zwischenspeichern der Bilder</string>
<string name="title_info">Ein eindeutiger beschreibender Titel für die Datei, der als Dateiname dient. Du kannst Klartext mit Leerzeichen verwenden. Gib nicht die Dateierweiterung mit an.</string>
<string name="description_info">Bitte beschreibe das Medium so gut wie möglich: Wo wurde es aufgenommen? Was zeigt es? Was ist der Kontext? Bitte beschreibe die Objekte oder Personen. Zeige Informationen auf, die nicht einfach erraten werden können, zum Beispiel die Tageszeit, falls es eine Landschaft ist. Falls das Medium etwas Ungewöhnliches zeigt, erkläre bitte, was es ungewöhnlich macht.</string>
<string name="use_external_storage">Externen Speicher verwenden</string>
<string name="use_external_storage_summary">Mit der In-App-Kamera aufgenommene Bilder auf deinem Gerät speichern</string>
</resources>

View file

@ -111,4 +111,11 @@
<string name="warning">Tembe kem</string>
<string name="yes">E</string>
<string name="no"></string>
<string name="media_detail_title">Sername</string>
<string name="media_detail_license">Lisans</string>
<string name="media_detail_coordinates">Koordinati</string>
<string name="cancel">Bıtexelne</string>
<string name="navigation_drawer_open">Ake</string>
<string name="navigation_item_home">Keye</string>
<string name="navigation_item_upload">Bar ke</string>
</resources>

View file

@ -129,7 +129,7 @@
<string name="detail_description_empty">Καμία περιγραφή</string>
<string name="detail_license_empty">Άγνωστη άδεια</string>
<string name="menu_refresh">Ανανέωση</string>
<string name="storage_permission_rationale">Απαιτούμενη άδεια: Ανάγνωση εξωτερικής αποθήκευσης. Η εφαρμογή δεν μπορεί να λειτουργήσει χωρίς αυτή.</string>
<string name="read_storage_permission_rationale">Απαιτούμενη άδεια: Ανάγνωση εξωτερικής αποθήκευσης. Η εφαρμογή δεν μπορεί να λειτουργήσει χωρίς αυτή.</string>
<string name="location_permission_rationale">Προαιρετική άδεια: Ανάκτηση τρέχουσας θέσης σας για προτάσεις κατηγοριών</string>
<string name="ok">Εντάξει</string>
<string name="title_activity_nearby">Κοντινοί Τόποι</string>

View file

@ -8,10 +8,10 @@
<string name="signup">Regístrate</string>
<string name="logging_in_title">Accediendo</string>
<string name="logging_in_message">Espera un momento…</string>
<string name="login_success">¡Inicio de sesión exitoso!</string>
<string name="login_failed">¡Inicio de sesión fallido!</string>
<string name="login_success">Acceso correcto.</string>
<string name="login_failed">Acceso fallido.</string>
<string name="upload_failed">No se encontró el archivo. Prueba con otro.</string>
<string name="authentication_failed">¡Autentificación fallida!</string>
<string name="authentication_failed">Falló la autenticación.</string>
<string name="uploading_started">¡Empenzando a subir!</string>
<string name="upload_completed_notification_title">¡Se subieron %1$s!</string>
<string name="upload_completed_notification_text">Pulsa para ver tu subida</string>
@ -78,7 +78,7 @@
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Normativa de privacidad&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Créditos&lt;/a&gt;</string>
<string name="title_activity_about">Acerca de</string>
<string name="menu_feedback">Enviar comentarios (por e-mail)</string>
<string name="menu_feedback">Enviar comentarios (por correo)</string>
<string name="no_email_client">No se ha instalado ningún cliente de correo</string>
<string name="provider_categories">Categorías usadas recientemente</string>
<string name="waiting_first_sync">Esperando la primera sincronización…</string>
@ -134,7 +134,8 @@
<string name="detail_description_empty">Sin descripción</string>
<string name="detail_license_empty">Licencia desconocida</string>
<string name="menu_refresh">Actualizar</string>
<string name="storage_permission_rationale">Permiso obligatorio: lectura de almacenamiento externo. La aplicación no puede funcionar sin él.</string>
<string name="read_storage_permission_rationale">Permiso obligatorio: lectura de almacenamiento externo. La aplicación no puede funcionar sin él.</string>
<string name="write_storage_permission_rationale">Permiso necesario: Escribir en almacenamiento externo. La aplicación no puede funcionar sin él.</string>
<string name="location_permission_rationale">Permiso opcional: obtener la ubicación actual para sugerir categorías</string>
<string name="ok">Aceptar</string>
<string name="title_activity_nearby">Lugares cercanos</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Error mientras se guardaban imágenes en la caché</string>
<string name="title_info">Un título único descriptivo para el archivo, que servirá como un nombre de archivo. Puede usar un lenguaje claro con espacios. No incluya la extensión del archivo.</string>
<string name="description_info">Por favor, describa el elemento multimedia tanto como sea posible: ¿dónde fue tomado?, ¿qué muestra?, ¿cuál es el contexto? Por favor, describa los objetos o personas. Ofrezca la información que no puede ser inferida tan facilmente, por ejemplo el momento del día si es un paisaje. Si el medio muestra algo inusual, explique qué lo hace insual.</string>
<string name="use_external_storage">Utilizar almacenamiento externo</string>
<string name="use_external_storage_summary">Guardar en el dispositivo imágenes capturadas con la cámara de la aplicación</string>
</resources>

View file

@ -20,11 +20,11 @@
<string name="upload_progress_notification_title_finishing">اتمام بارگذاری %1$s</string>
<string name="upload_failed_notification_title">%1$s بارگذاری نشد</string>
<string name="upload_failed_notification_subtitle">برای دیدن انگشت بزنید</string>
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
<item quantity="one">یک پرونده در حال بارگذاری</item>
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">%d پرونده در حال بارگذاری</item>
<item quantity="other">%d پرونده در حال بارگذاری</item>
</plurals>
<string name="title_activity_contributions" fuzzy="true">بارگذاری‌های من</string>
<string name="title_activity_contributions">بارگذاری‌های اخیر من</string>
<string name="contribution_state_queued">در صف</string>
<string name="contribution_state_failed">ناموفق بود</string>
<string name="contribution_state_in_progress">%1$d٪ کامل شد</string>
@ -42,6 +42,7 @@
<string name="login_failed_password">ناتوانی در ورود - لطفاً گذرواژه‌یتان را بررسی کنید</string>
<string name="login_failed_throttled">تلاش ناموفق بیش از حد. لطفاً چند دقیقهٔ دیگر دوباره تلاش کنید</string>
<string name="login_failed_blocked">پوزش، کاربر در ویکی‌انبار بسته شده‌است</string>
<string name="login_failed_2fa_needed">باید تأیید دومرحله‌ای را فعال کنید.</string>
<string name="login_failed_generic">ورود ناموفق بود</string>
<string name="share_upload_button">بارگذاری</string>
<string name="multiple_share_base_title">نام این مجموعه</string>
@ -49,17 +50,21 @@
<string name="menu_upload_single">بارگذاری</string>
<string name="categories_search_text_hint">جستجوی رده‌ها</string>
<string name="menu_save_categories">ذخیره</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">هنوز بارگذاری نشده است</item>
<item quantity="one">یک بارگذاری شد</item>
<string name="refresh_button">تازه کردن</string>
<string name="gps_disabled">مکان‌یاب در دستگاه شما خاموش است. آیا دوست دارید فعال شود؟</string>
<string name="enable_gps">فعال کردن مکان‌یاب</string>
<string name="contributions_subtitle_zero">هنوز هیچ بارگذاری</string>
<plurals name="contributions_subtitle">
<item quantity="zero">\@string/contributions_subtitle_zero</item>
<item quantity="one">بارگذاری شد</item>
<item quantity="other">%d بارگذاری شد</item>
</plurals>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one">شروع بارگذاری پرونده</item>
<item quantity="other">شروع بارگذاری %d پرونده</item>
<plurals name="starting_multiple_uploads">
<item quantity="one">شروع %d بارگذاری پرونده</item>
<item quantity="other">شروع بارگذاری %d پرونده</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<item quantity="one">۱ بارگذاری</item>
<plurals name="multiple_uploads_title">
<item quantity="one">%d بارگذاری</item>
<item quantity="other">%d بارگذاری</item>
</plurals>
<string name="categories_not_found">رده‌ای منطبق با %1$s یافت نشد</string>
@ -68,9 +73,10 @@
<string name="title_activity_settings">تنظیمات</string>
<string name="title_activity_signup">ثبت نام</string>
<string name="menu_about">درباره</string>
<string name="about_license" fuzzy="true">نرم‌افزار متن‌باز آزاد تحت &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;مجوز آپاچی نسخهٔ ۲&lt;/a&gt;\n\nویکی‌انبار و نشانش یک نشان تجاری‌ست و با اجازهٔ بنیاد ویکی‌مدیا استفاده می‌شود. ما زیرمجموعه یا شعبهٔ بنیاد نیستیم.</string>
<string name="about_license">نرم‌افزار متن‌باز آزاد تحت &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;مجوز آپاچی نسخهٔ ۲&lt;/a&gt;\n\n%1$s و نشانش یک نشان تجاری‌ست و با اجازهٔ بنیاد ویکی‌مدیا استفاده می‌شود. ما زیرمجموعه یا شعبهٔ بنیاد نیستیم.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Source&lt;/a&gt; and &lt;a href=\"https://commons-app.github.io/\"&gt;وب‌سایت&lt;/a&gt; در گیت‌هاب. ایجاد یک &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;درخواست در گیت‌هاب&lt;/a&gt; برای گزارش باگ و یا پیشنهاد یک خصوصیت جدید.</string>
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;سیاست حفظ حریم خصوصی&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;مجوز&lt;/a&gt;</string>
<string name="title_activity_about">درباره</string>
<string name="menu_feedback">ارسال بازخورد (از طریق ایمیل)</string>
<string name="no_email_client">نرم‌افزار ایمیل نصب نیست</string>
@ -80,11 +86,16 @@
<string name="menu_retry_upload">تلاش مجدد</string>
<string name="menu_cancel_upload">لغو</string>
<string name="share_license_summary">این نگاره تحت مجوز %1$s است</string>
<string name="media_upload_policy">با بارگذاری این تصویر، تأیید می‌کنم که این اثر کار خودم است و از محتوای دارای حق تکثیر یا سلفی برای ایجاد آن استفاده نکرده‌ام و شرایط ذکر شده در By submitting this picture, I declare that this is my own work, that it does not contain copyrighted material or selfies, and otherwise adheres to &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;سیاست‌های ویکی‌انبار&lt;/a&gt; را رعایت می‌کند.</string>
<string name="menu_download">دریافت</string>
<string name="preference_license">مجوز</string>
<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">CC Attribution-ShareAlike 4.0</string>
<string name="license_name_cc_by_four">Attribution 4.0</string>
<string name="license_name_cc_by_sa">CC Attribution-ShareAlike 3.0</string>
<string name="license_name_cc_by">CC Attribution 3.0</string>
<string name="license_name_cc0">CC0</string>
@ -123,12 +134,67 @@
<string name="detail_description_empty">بدون توضیحات</string>
<string name="detail_license_empty">مجوز ناشناخته</string>
<string name="menu_refresh">تازه‌کردن</string>
<string name="storage_permission_rationale">اجازه‌های مورد نیاز: مطالعهٔ حافظهٔ خارجی. اپلیکیشن بدون آن نمی‌تواند کار کند.</string>
<string name="read_storage_permission_rationale">اجازه‌های مورد نیاز: مطالعهٔ حافظهٔ خارجی. اپلیکیشن بدون آن نمی‌تواند کار کند.</string>
<string name="write_storage_permission_rationale">اجازه‌های مورد نیاز: نوشتن حافظهٔ خارجی. اپلیکیشن بدون آن نمی‌تواند کار کند.</string>
<string name="location_permission_rationale">اجازه‌های اختیاری: دریافت موقعیت برای پیشنهاد رده</string>
<string name="ok">تأیید</string>
<string name="title_activity_nearby">مکان‌‌های اطراف</string>
<string name="no_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="media_detail_description_explanation">توضیحات رسانه اینجا می‌روند. امکان دارد طولانی باشد و نیاز به چند خط شدن داشته باشد. امیدواریم خوب دیده شود.</string>
<string name="media_detail_uploaded_date">تاریخ بارگذاری</string>
<string name="media_detail_license">مجوز</string>
<string name="media_detail_coordinates">مختصات‌ها</string>
<string name="media_detail_coordinates_empty">ارائه نشده است</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>
<string name="_2fa_code">کد 2FA</string>
<string name="number_of_uploads">محدودیت بارگذاری اخیر من</string>
<string name="maximum_limit">حداکثر محدودیت</string>
<string name="maximum_limit_alert">عدم توانایی در نمایش بیش از ۵۰۰ مورد</string>
<string name="set_limit">تنظیم محدودیت بارگذاری‌های اخیر</string>
<string name="login_failed_2fa_not_supported">تأیید دومرحله‌ای الان پشتیبانی نمی‌شود.</string>
<string name="logout_verification">آیا واقعاً قصد خروج از سامانه را دارید؟</string>
<string name="commons_logo">نشان ویکی‌انبار</string>
<string name="background_image">تصویر پس‌زمینه</string>
<string name="mediaimage_failed">خطای تصویر رسانه</string>
<string name="no_image_found">تصویری یافت نشد</string>
<string name="upload_image">بارگذاری تصویر</string>
<string name="welcome_image_mount_zao">کوه زائو</string>
<string name="welcome_image_llamas">لاما</string>
<string name="welcome_image_rainbow_bridge">رینبو بریج</string>
<string name="welcome_image_tulip">لاله</string>
<string name="welcome_image_no_selfies">سلفی نه</string>
<string name="welcome_image_proprietary">تصویر اختصاصی</string>
<string name="welcome_image_welcome_wikipedia">به ویکی‌پدیا خوش‌آمدید</string>
<string name="welcome_image_welcome_copyright">حق‌تکثیر خوش‌آمدگویی</string>
<string name="welcome_image_sydney_opera_house">خانه اپرای سیدنی</string>
<string name="cancel">لغو</string>
<string name="navigation_drawer_open">باز کردن</string>
<string name="navigation_drawer_close">بستن</string>
<string name="navigation_item_home">خانه</string>
<string name="navigation_item_upload">بارگذاری</string>
<string name="navigation_item_nearby">در نزدیکی</string>
<string name="navigation_item_about">درباره</string>
<string name="navigation_item_settings">تنظیمات</string>
<string name="navigation_item_feedback">بازخورد</string>
<string name="navigation_item_logout">خروج</string>
<string name="navigation_item_info">آموزش</string>
<string name="nearby_needs_permissions">مکان‌های اطراف بدون اجازه دادن به مکان‌یاب مقدور نیست</string>
<string name="no_description_found">توضیحی یافت نشد</string>
<string name="nearby_info_menu_commons_article">صفحهٔ دروند در ویکی‌انبار</string>
<string name="nearby_info_menu_wikidata_article">آیتم ویکی‌داده</string>
<string name="error_while_cache">خطا در زمان دریافت تصاویر</string>
<string name="title_info">عنوانی توصیفی و یکتا برای پرونده که به عنوان نام پرونده در نظر گرفته خواهد شد. ترجیحاً به زبان ساده باشد، می‌توانید فاصله هم به کار ببرید. پسوند پرونده را ننویسید.</string>
<string name="description_info">لطفاً تصویر را تا حد توان شرح دهید. کجا گرفته شده‌است؟ شامل چه چیزی می‌شود؟ لطفاً اشیا یا افراد را شرح دهید. اطلاعاتی که به راحتی قابل مشاهده هستند را صرفه‌نظر کنید. اگر چیزی در تصویر غیر طبیعی به نظر می‌رسد آن را شرح دهید.</string>
<string name="use_external_storage">استفاده از حافظهٔ خارجی</string>
<string name="use_external_storage_summary">ذخیرهٔ تصویرهای گرفته شده توسط دوربین درونکار اپلیکیشن بر روی دستگاه شما</string>
</resources>

View file

@ -14,7 +14,7 @@
<string name="authentication_failed">Tunnistautuminen epäonnistui!</string>
<string name="uploading_started">Tallentaminen aloitettiin!</string>
<string name="upload_completed_notification_title">%1$s tallennettiin!</string>
<string name="upload_completed_notification_text">Napauta katsoaksesi tallennuksen</string>
<string name="upload_completed_notification_text">Napauta katsoaksesi tallennusta</string>
<string name="upload_progress_notification_title_start">Aloitetaan tiedoston %1$s tallennusta</string>
<string name="upload_progress_notification_title_in_progress">Tallennetaan %1$s</string>
<string name="upload_progress_notification_title_finishing">%1$s tallennettu</string>
@ -29,7 +29,7 @@
<string name="contribution_state_failed">Epäonnistui</string>
<string name="contribution_state_in_progress">%1$d%% valmis</string>
<string name="contribution_state_starting">Tallennetaan</string>
<string name="menu_from_gallery">Olemassaoleva</string>
<string name="menu_from_gallery">Galleriasta</string>
<string name="menu_from_camera">Ota kuva</string>
<string name="menu_nearby">Lähistöllä</string>
<string name="provider_contributions">Omat tallennukset</string>
@ -39,7 +39,7 @@
<string name="share_description_hint">Kuvaus</string>
<string name="login_failed_network">Kirjautuminen epäonnistui - verkkovirhe</string>
<string name="login_failed_username">Kirjautuminen epäonnistui - tarkista käyttäjätunnus</string>
<string name="login_failed_password">Kirjautuminen epäonnistui - tarkista salasana</string>
<string name="login_failed_password">Kirjautuminen epäonnistui - tarkista salasanasi</string>
<string name="login_failed_throttled">Liikaa epäonnistuneita yrityksiä. Yritä uudelleen parin minuutin kuluttua.</string>
<string name="login_failed_blocked">Pahoittelut, tämä käyttäjä on estetty Commonsissa</string>
<string name="login_failed_2fa_needed">Anna kaksivaiheisen tunnistuksen koodi.</string>
@ -54,18 +54,18 @@
<string name="gps_disabled">GPS ei ole käytössä. Haluatko ottaa sen käyttöön?</string>
<string name="enable_gps">Ota GPS käyttöön</string>
<string name="contributions_subtitle_zero">Ei tallennuksia vielä</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">Ei tallennuksia</item>
<item quantity="one">1 tallennus</item>
<plurals name="contributions_subtitle">
<item quantity="zero">\@string/contributions_subtitle_zero</item>
<item quantity="one">%d tallennus</item>
<item quantity="other">%d tallennusta</item>
</plurals>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one">1 tallennus alkaa</item>
<item quantity="other">%d tallennusta alkaa</item>
<plurals name="starting_multiple_uploads">
<item quantity="one">aloitetaan %d tallennus</item>
<item quantity="other">aloitetaan %d tallennusta</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<item quantity="one">1 lataus</item>
<item quantity="other">%d latausta</item>
<plurals name="multiple_uploads_title">
<item quantity="one">%d tallennus</item>
<item quantity="other">%d tallennusta</item>
</plurals>
<string name="categories_not_found">Luokkaa %1$s ei löytynyt</string>
<string name="categories_skip_explanation">Lisää luokkia tehdäksesi kuvistasi helpommin löydettäviä.\n\nAloita kirjoittaminen lisätäksesi luokkia.\nNapauta tätä viestiä (tai paina takaisin) ohittaaksesi tämän vaiheen.</string>
@ -73,7 +73,7 @@
<string name="title_activity_settings">Asetukset</string>
<string name="title_activity_signup">Rekisteröidy</string>
<string name="menu_about">Tietoja</string>
<string name="about_license" fuzzy="true">Tämä on vapaan lähdekoodin ohjelmisto, joka on julkaistu &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache License v2&lt;/a&gt; -lisenssin alaisena. Wikimedia Commons ja sen logo ovat Wikimedia Foundationin tavaramerkkejä ja niitä käytetään Wikimedia Foundationin luvalla. Emme ole hyväksyttyjä tai sidoksissa Wikimedia Foundationioniin.</string>
<string name="about_license">Tämä on vapaan lähdekoodin ohjelmisto, joka on julkaistu &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache License v2&lt;/a&gt; -lisenssin alaisena. Wikimedia Commons ja sen logo ovat Wikimedia Foundationin tavaramerkkejä ja niitä käytetään Wikimedia Foundationin luvalla. Emme ole hyväksyttyjä tai sidoksissa Wikimedia Foundationioniin.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Lähde&lt;/a&gt; ja &lt;a href=\"https://commons-app.github.io/\"&gt;nettisivusto&lt;/a&gt; GitHubissa. Luo uusi &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;GitHub-issue&lt;/a&gt; bugiraporteille ja ehdotuksille.</string>
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Yksityisyydensuoja&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Tekijät&lt;/a&gt;</string>
@ -86,6 +86,7 @@
<string name="menu_retry_upload">Yritä uudelleen</string>
<string name="menu_cancel_upload">Peruuta</string>
<string name="share_license_summary">Tiedosto tallennetaan lisenssin %1$s ehtojen mukaisesti.</string>
<string name="media_upload_policy">Lisäämällä kuvan, ilmoitan tämän olevan oma työ ja että se ei sisällä tekijänoikeuden alaista materiaalia tai selfietä ja muuten noudattaa &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;Wikimedia Commons policies&lt;/a&gt;.</string>
<string name="menu_download">Lataa</string>
<string name="preference_license">Lisenssi</string>
<string name="use_previous">Käytä edellistä otsikkoa/kuvausta</string>
@ -115,15 +116,15 @@
<string name="license_name_cc_zero">CC0</string>
<string name="tutorial_1_text">Wikimedia Commonsiin on tallennettu lähes kaikki Wikipediassa käytetyt kuvat.</string>
<string name="tutorial_1_subtext">Kuvasi auttavat ihmisiä kaikkialta maailmasta ymmärtämään maailmaa!</string>
<string name="tutorial_2_text">Tallenna kuvia, jotka sinä itse olet kuvannot tai luonut:</string>
<string name="tutorial_2_text">Tallenna kuvia, jotka sinä itse olet ottanut tai luonut:</string>
<string name="tutorial_2_subtext">- Luontokohteet (kukat, eläimet, vuoret)\n- Hyödylliset esineet (polkupyörät, rautatieasemat)\n- Kuuluisat henkilöt (kaupunginjohtajasi, tapaamasi olympiaurheilijat)</string>
<string name="tutorial_3_text">Ä tallenna seuraavia:</string>
<string name="tutorial_3_text">Ä tallenna seuraavia:</string>
<string name="tutorial_3_subtext">- Selfiet tai kuvat ystävistäsi\n- Netistä ladatut kuvat\n- Kuvakaappaukset kaupallisista sovelluksista</string>
<string name="tutorial_4_text">Tallennusesimerkki:</string>
<string name="tutorial_4_subtext">- Nimi: Sydneyn operatalo\n- Kuvaus: Sydneyn oopperatalo katsottuna lahden toisella puolella\n- Luokat: Sydneyn oopperatalo, Sydneyn oopperatalo lännestä, Sydneyn oopperatalo remote views</string>
<string name="welcome_wikipedia_text">Herätä Wikipedia-artikkelit eloon kuvillasi! Tuo kuvasi Wikipediaan.</string>
<string name="welcome_wikipedia_subtext">Wikipedian kuvat tulevat Wikimedia Commonsista.</string>
<string name="welcome_copyright_text">Kuvat auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä.</string>
<string name="welcome_copyright_text">Kuvasi auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä.</string>
<string name="welcome_copyright_subtext">Vältä tekijänoikeuksien alaista materiaalia, kuten julisteita, kirjan kansia ja useimpia Internetistä löydettyjä kuvia.</string>
<string name="welcome_final_text">Luuletko ymmärtäneesi tämän?</string>
<string name="welcome_final_button_text">Kyllä!</string>
@ -133,9 +134,10 @@
<string name="detail_description_empty">Ei kuvausta</string>
<string name="detail_license_empty">Tuntematon lisenssi</string>
<string name="menu_refresh">Päivitä</string>
<string name="storage_permission_rationale">Vaadittu oikeus: Ulkoisen tallennustilan luku. Appi ei toimi ilman tätä oikeutta.</string>
<string name="read_storage_permission_rationale">Vaadittu oikeus: Ulkoisen tallennustilan luku. Appi ei toimi ilman tätä oikeutta.</string>
<string name="location_permission_rationale">Valinnainen lupa: Saada tämänhetkinen sijainti loukkasuosituksia varten.</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Läheiset paikat</string>
<string name="title_activity_nearby">Lähellä olevat paikat</string>
<string name="no_nearby">Lähistöltä ei löytynyt paikkoja</string>
<string name="warning">Varoitus</string>
<string name="file_exists">Tämä tiedosto on jo Wikimedia Commonsissa. Haluatko varmasti jatkaa?</string>
@ -147,14 +149,24 @@
<string name="media_detail_uploaded_date">Tallennuspäivämäärä</string>
<string name="media_detail_license">Lisenssi</string>
<string name="media_detail_coordinates">Koordinaatit</string>
<string name="media_detail_coordinates_empty">ei annettu</string>
<string name="become_a_tester_title">Ryhdy beetatestaajaksi</string>
<string name="use_wikidata">Käytä Wikidataa</string>
<string name="use_wikidata_summary">(Varoitus: poiskytkeminen voi aiheuttaa suuren mobiilidatankäytön)</string>
<string name="maximum_limit">Maksimimäärä</string>
<string name="maximum_limit_alert">Ei voida näyttää enempää, kuin 500</string>
<string name="login_failed_2fa_not_supported">Kaksivaiheinen tunnistus ei ole vielä tuettu.</string>
<string name="logout_verification">Haluatko kirjautua ulos?</string>
<string name="logout_verification">Haluatko varmasti kirjautua ulos?</string>
<string name="commons_logo">Commons Logo</string>
<string name="background_image">Taustakuva</string>
<string name="no_image_found">Kuvaa ei löytynyt</string>
<string name="upload_image">Lataa kuva</string>
<string name="welcome_image_mount_zao">Zao-vuori</string>
<string name="welcome_image_rainbow_bridge">Sateenkaarisilta</string>
<string name="welcome_image_tulip">Tulppaani</string>
<string name="welcome_image_no_selfies">Ei selfieitä</string>
<string name="welcome_image_welcome_wikipedia">Tervetuloa Wikipediaan</string>
<string name="welcome_image_welcome_copyright">Tervetuloa tekijänoikeus</string>
<string name="welcome_image_sydney_opera_house">Sydneyn oopperatalo</string>
<string name="cancel">Peru</string>
<string name="navigation_drawer_open">Avaa</string>
@ -166,6 +178,9 @@
<string name="navigation_item_settings">Asetukset</string>
<string name="navigation_item_feedback">Palaute</string>
<string name="navigation_item_logout">Kirjaudu ulos</string>
<string name="navigation_item_info">Opas</string>
<string name="nearby_needs_permissions">Lähellä olevia paikkoja ei voida näyttää ilman sijaintilupaa</string>
<string name="nearby_info_menu_commons_article">Commons-tiedostosivu</string>
<string name="nearby_info_menu_wikidata_article">Wikidata-kohde</string>
<string name="title_info">Tiedoston yksilöllinen ja kuvaava otsikko, jota käytetään tiedostonimenä. Voit käyttää tavallista kieltä välilyönnein. Älä sisällytä tiedoston päätettä.</string>
</resources>

View file

@ -114,7 +114,7 @@
<string name="license_name_cc_by_sa_4_0">CC BY-SA 4.0</string>
<string name="license_name_cc_by_4_0">CC BY 4.0</string>
<string name="license_name_cc_zero">CC Zéro</string>
<string name="tutorial_1_text">Wikimédia Communs héberge la plupart des images qui sont utilisées dans Wikipédia</string>
<string name="tutorial_1_text">Wikimedia Commons héberge la plupart des images qui sont utilisées dans Wikipédia.</string>
<string name="tutorial_1_subtext">Vos images aident à éduquer les gens dans le monde entier!</string>
<string name="tutorial_2_text">Veuillez téléverser des images qui sont prises ou créées entièrement par vous :</string>
<string name="tutorial_2_subtext">- Objets naturels (fleurs, animaux, montagnes) \n- Objets utiles (bicyclettes, gares ferroviaires) \n- Personnes célèbres (votre maire, les athlètes olympiques que vous avez rencontrés)</string>
@ -134,13 +134,14 @@
<string name="detail_description_empty">Aucune description</string>
<string name="detail_license_empty">Licence inconnue</string>
<string name="menu_refresh">Rafraîchir</string>
<string name="storage_permission_rationale">Autorisation nécessaire : Lire un stockage externe. Lapplication ne peut pas fonctionner sans cela.</string>
<string name="read_storage_permission_rationale">Autorisation nécessaire : Lire un stockage externe. Lapplication ne peut pas fonctionner sans cela.</string>
<string name="write_storage_permission_rationale">Permission obligatoire : Écriture sur stockage externe. Lapplication ne peut pas fonctionner sans cela.</string>
<string name="location_permission_rationale">Autorisation facultative : Obtenir lemplacement actuel pour des suggestions de catégorie</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Endroits à proximité</string>
<string name="no_nearby">Rien trouvé dans le voisinage</string>
<string name="warning">Avertissement</string>
<string name="file_exists">Ce fichier existe déjà sur Communs. Êtes-vous sûr de vouloir continuer?</string>
<string name="file_exists">Ce fichier existe déjà sur Commons. Êtes-vous sûr de vouloir continuer?</string>
<string name="yes">Oui</string>
<string name="no">Non</string>
<string name="media_detail_title">Titre</string>
@ -162,7 +163,7 @@
<string name="set_limit">Fixer la limite de téléversement récent</string>
<string name="login_failed_2fa_not_supported">Lauthentification à deux facteurs nest pas prise en charge pour le moment.</string>
<string name="logout_verification">Voulez-vous vraiment vous déconnecter?</string>
<string name="commons_logo">Logo de Communs</string>
<string name="commons_logo">Logo de Commons</string>
<string name="background_image">Image de fond</string>
<string name="mediaimage_failed">Échec sur limage du média</string>
<string name="no_image_found">Aucune image trouvée</string>
@ -189,9 +190,11 @@
<string name="navigation_item_info">Tutoriel</string>
<string name="nearby_needs_permissions">Les endroits proches ne peuvent pas être affichés si vous ne partagez pas votre position géographique.</string>
<string name="no_description_found">aucune description trouvée</string>
<string name="nearby_info_menu_commons_article">Page des fichiers de Communs</string>
<string name="nearby_info_menu_commons_article">Page des fichiers de Commons</string>
<string name="nearby_info_menu_wikidata_article">Élément de Wikidata</string>
<string name="error_while_cache">Erreur en mettant les images en cache</string>
<string name="title_info">Un titre descriptif unique pour le fichier, qui servira de nom de fichier. Vous pouvez utiliser un langage simple avec des espaces. Nincluez pas lextension du fichier</string>
<string name="description_info">Veuillez décrire le média autant que possible : Où a-t-il été enregistré? Que montre-t-il? Quel est le contexte? Veuillez décrire les objets ou les personnes. Révélez les informations qui ne peuvent pas être devinées facilement, par exemple lheure de la journée si cest un paysage. Si le média montre quelque chose dinhabituel, veuillez expliquer ce qui le rend exceptionnel.</string>
<string name="use_external_storage">Utiliser le stockage externe</string>
<string name="use_external_storage_summary">Enregistrer les images prises avec lappareil photo de votre appareil</string>
</resources>

View file

@ -133,7 +133,7 @@
<string name="detail_description_empty">Nian beskriiwang</string>
<string name="detail_license_empty">Ünbekäänd lisens</string>
<string name="menu_refresh">Nei loose</string>
<string name="storage_permission_rationale">Ferlangd rochten: Ekstern spiiker lees. Det app koon saner detdiar rocht ei werke.</string>
<string name="read_storage_permission_rationale">Ferlangd rochten: Ekstern seekrang lees. Det app koon saner detdiar rocht ei werke.</string>
<string name="location_permission_rationale">Mögelk rocht: Rept di aktuel plak för kategoriiföörslacher ap.</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Steeden naibi</string>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Sen descrición</string>
<string name="detail_license_empty">Licenza descoñecida</string>
<string name="menu_refresh">Refrescar</string>
<string name="storage_permission_rationale">Permiso necesarioː ler un almacenamento externo. A aplicación non pode funcionar sen isto.</string>
<string name="read_storage_permission_rationale">Permiso necesarioː ler un almacenamento externo. A aplicación non pode funcionar sen isto.</string>
<string name="write_storage_permission_rationale">Permiso necesario: Escribir en almacenamento externo. A aplicación non pode funcionar sen el.</string>
<string name="location_permission_rationale">Permiso opcionalː obter a localización actual para suxerir categorías</string>
<string name="ok">Aceptar</string>
<string name="title_activity_nearby">Lugares próximos</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Erro mentras se gardaban as imaxes na caché</string>
<string name="title_info">Un título único descritivo para o ficheiro, que servirá como un nome de ficheiro. Pode usar unha linguaxe clara con espazos. Non inclúa a extensión do ficheiro</string>
<string name="description_info">Por favor, describa o ficheiro todo o posibleː Onde se gravou? Cal é o contexto? Por favor, describa os obxectos ou persoas. Indique información que non pode ser adiviñada de forma doada, por exemplo, a hora do día se é unha paisaxe. Se o ficheiro amosa algo pouco habitual, por favor, explique que é o que o fai excepcional.</string>
<string name="use_external_storage">Usar o almacenamento externo</string>
<string name="use_external_storage_summary">Gardar as imaxes capturadas coa cámara do seu dispositivo</string>
</resources>

View file

@ -42,6 +42,7 @@
<string name="login_failed_password">प्रवेश नहीं हो रहा - कृपया अपना पासवर्ड जाँचें</string>
<string name="login_failed_throttled">ढेर सारे असफल प्रयास होने के कारण कुछ मिनटों के बाद प्रयास करें।</string>
<string name="login_failed_blocked">क्षमा करें, यह सदस्य कॉमन्स में अवरोधित है</string>
<string name="login_failed_2fa_needed">आपको अपना दो कारक प्रमाणन कोड प्रदान करना होगा।</string>
<string name="login_failed_generic">प्रवेश विफल</string>
<string name="share_upload_button">अपलोड</string>
<string name="multiple_share_base_title">सूचियों को नाम दें</string>
@ -50,11 +51,10 @@
<string name="categories_search_text_hint">श्रेणियाँ खोजें</string>
<string name="menu_save_categories">सहेजें</string>
<string name="refresh_button">ताजा करें</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">कोई अपलोड नहीं</item>
<item quantity="one">%d अपलोड</item>
<item quantity="other">%d अपलोड</item>
</plurals>
<string name="gps_disabled">आपके डिवाइस में जीपीएस अक्षम है। क्या आप इसे सक्षम करना चाहेंगे?</string>
<string name="enable_gps">जीपीएस सक्षम करें</string>
<string name="contributions_subtitle_zero">अभी तक कोई अपलोड नहीं</string>
<string name="contributions_subtitle">{{PLURAL | शून्य = @ स्ट्रिंग / योगदान_उपशीर्षक_शुन्य | एक =% d अपलोड |% d अपलोड्स}}</string>
<plurals name="starting_multiple_uploads">
<item quantity="one">%d अपलोड शुरू</item>
<item quantity="other">%d अपलोड शुरू</item>
@ -69,10 +69,10 @@
<string name="title_activity_settings">पसंद</string>
<string name="title_activity_signup">खाता खोलें</string>
<string name="menu_about">परिचय</string>
<string name="about_license" fuzzy="true">मुक्त स्रोत सॉफ्टवेयर जो &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;अपाचे लाइसेन्स&lt;/a&gt; के अंतर्गत जारी किया गया है। विकिमीडिया कॉमन्स और इसका लोगो विकिमीडिया संस्था का व्यापारिक चिह्न है और इसके मर्जी से ही उपयोग किया जाना चाहिए। हम किसी भी प्रकार से विकिमीडिया संस्था से जुड़े नहीं हैं।</string>
<string name="about_license">मुक्त स्रोत सॉफ्टवेयर जो &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;अपाचे लाइसेन्स&lt;/a&gt; के अंतर्गत जारी किया गया है। %1$s और इसका लोगो विकिमीडिया संस्था का व्यापारिक चिह्न है और इसके मर्जी से ही उपयोग किया जाना चाहिए। हम किसी भी प्रकार से विकिमीडिया संस्था से जुड़े नहीं हैं।</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;स्रोत&lt;/a&gt; और &lt;a href=\"https://commons-app.github.io/\"&gt;वेबसाइट&lt;/a&gt; गिटहब में है और त्रुटि व सुझाव हेतु &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;गिटहब समस्या&lt;/a&gt; देखें।</string>
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;गोपनियता नीति&lt;/a&gt;</string>
<string name="about_credits" fuzzy="true">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;श्रेय&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;श्रेय&lt;/a&gt;</string>
<string name="title_activity_about">परिचय</string>
<string name="menu_feedback">प्रतिक्रिया दें (ईमेल द्वारा)</string>
<string name="no_email_client">कोई ईमेल ग्राहक स्थापित नहीं</string>
@ -82,6 +82,7 @@
<string name="menu_retry_upload">फिर प्रयास करें</string>
<string name="menu_cancel_upload">रद्द करें</string>
<string name="share_license_summary">इस छवि का लाइसेन्स %1$s के अंतर्गत है।</string>
<string name="media_upload_policy">इस तस्वीर को सबमिट करके, मैं घोषणा करता हूं कि यह मेरा अपना काम है, इसमें कॉपीराइट सामग्री या सेल्फी नहीं है, और अन्यथा &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;विकीमीडिया कॉमन्स नीतियां का पालन करता हूँ &lt;/a&gt;</string>
<string name="menu_download">डाउनलोड</string>
<string name="preference_license">लाइसेन्स</string>
<string name="use_previous">पिछले शीर्षक/विवरण का उपयोग करें</string>
@ -129,10 +130,12 @@
<string name="detail_description_empty">कोई विवरण नहीं</string>
<string name="detail_license_empty">अज्ञात लाइसेन्स</string>
<string name="menu_refresh">ताजा करें</string>
<string name="storage_permission_rationale">अनिवार्य अनुमति: बाहरी स्मृति पढ़ें। एप इसके बिना कार्य नहीं करेगा।</string>
<string name="read_storage_permission_rationale">अनिवार्य अनुमति: बाहरी स्मृति पढ़ें। एप इसके बिना कार्य नहीं करेगा।</string>
<string name="write_storage_permission_rationale">अनिवार्य अनुमति:बाहरी कंप्यूटर स्टोरेज लिखना|इसके बिना एप कार्य नहीं करेगा।</string>
<string name="location_permission_rationale">वैकल्पिक अनुमति: श्रेणी सुझाव हेतु वर्तमान स्थान ज्ञात करें</string>
<string name="ok">ठीक है</string>
<string name="title_activity_nearby">आसपास के स्थान</string>
<string name="no_nearby">पास के कोई भी स्थान नहीं मिले</string>
<string name="warning">चेतावनी</string>
<string name="file_exists">यह फ़ाइल कॉमन्स पर पहले से है। क्या आप फिर भी आगे बढ़ना चाहते हैं?</string>
<string name="yes">हाँ</string>
@ -140,9 +143,54 @@
<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="media_detail_uploaded_date">अपलोड की गई दिनांक</string>
<string name="media_detail_license">लाइसेंस</string>
<string name="media_detail_coordinates">निर्देशांक</string>
<string name="media_detail_coordinates_empty">कुछ नहीं प्रदान किया गया</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>
<string name="_2fa_code">2 एफए कोड</string>
<string name="number_of_uploads">मेरी हाल ही की अपलोड सीमा</string>
<string name="maximum_limit">अधिकतम सीमा</string>
<string name="maximum_limit_alert">500 से अधिक प्रदर्शित करने में असमर्थ</string>
<string name="set_limit">हाल ही में अपलोड सीमा सेट करें</string>
<string name="login_failed_2fa_not_supported">दो कारक प्रमाणीकरण वर्तमान में समर्थित नहीं है</string>
<string name="logout_verification">क्या आप वास्तव में निकास करना चाहते हैं?</string>
<string name="commons_logo">कॉमन्स का प्रतीक चिन्ह</string>
<string name="background_image">पृष्ठभूमि छवि</string>
<string name="mediaimage_failed">मीडिया छवि विफल</string>
<string name="no_image_found">कोई छवि नहीं मिली</string>
<string name="upload_image">तस्वीर डालिये</string>
<string name="welcome_image_mount_zao">माउंट ज़ाओ</string>
<string name="welcome_image_llamas">ल्लामस</string>
<string name="welcome_image_rainbow_bridge">इंद्रधनुष के पुल</string>
<string name="welcome_image_tulip">ट्यूलिप</string>
<string name="welcome_image_no_selfies">कोई सेल्फीज़ नहीं</string>
<string name="welcome_image_proprietary">स्वामित्व छवि</string>
<string name="welcome_image_welcome_wikipedia">विकिपीडिया में स्वागत है</string>
<string name="welcome_image_welcome_copyright">प्रतिलिप्याधिकार में स्वागत है</string>
<string name="welcome_image_sydney_opera_house">सिडनी का ओपेरा हाउस</string>
<string name="cancel">रद्द करें</string>
<string name="navigation_drawer_open">खोलें</string>
<string name="navigation_drawer_close">बंद करें</string>
<string name="navigation_item_home">घर</string>
<string name="navigation_item_upload">अपलोड करें</string>
<string name="navigation_item_nearby">पास में</string>
<string name="navigation_item_about">के बारे में</string>
<string name="navigation_item_settings">सेटिंग्स</string>
<string name="navigation_item_feedback">आपके सुझाव</string>
<string name="navigation_item_logout">प्रस्थान करें</string>
<string name="navigation_item_info">अनुशिक्षण</string>
<string name="nearby_needs_permissions">आस-पास के स्थान बिना स्थान अनुमतियों के प्रदर्शित नहीं किए जा सकते हैं</string>
<string name="no_description_found">कोई विवरण नहीं मिला</string>
<string name="nearby_info_menu_commons_article">कॉमन्स फाइल पृष्ठ</string>
<string name="nearby_info_menu_wikidata_article">विकिडाटा वस्तु</string>
<string name="error_while_cache">चित्र कैशिंग करते समय त्रुटि</string>
<string name="title_info">फ़ाइल के लिए एक अद्वितीय वर्णनात्मक शीर्षक, जो एक फ़ाइल नाम के रूप में काम करेगा। आप रिक्त स्थान के साथ सादे भाषा का उपयोग कर सकते हैं। फ़ाइल विस्तार शामिल न करें</string>
<string name="description_info">कृपया मीडिया जितना संभव हो उतना बताएं: यह कहां लिया गया? यह क्या दिखाता है? संदर्भ क्या है? कृपया वस्तुओं या व्यक्तियों का वर्णन करें। ऐसी जानकारी का खुलासा करें जिसे आसानी से अनुमानित नहीं किया जा सकता, उदाहरण के लिए दिन का समय यदि यह परिदृश्य है। अगर मीडिया कुछ असामान्य दिखाता है, तो कृपया बताएं कि इसे क्या असामान्य बनाता है।</string>
<string name="use_external_storage">बाहरी स्टॉरज का पृयोग करे।</string>
<string name="use_external_storage_summary">आप अपने डिवाइस के इन-ऐप कैमरा से ली गई तस्वीरों को सहेजें।</string>
</resources>

View file

@ -133,7 +133,7 @@
<string name="detail_description_empty">Nincs leírás</string>
<string name="detail_license_empty">Ismeretlen licenc</string>
<string name="menu_refresh">Frissítés</string>
<string name="storage_permission_rationale">Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül.</string>
<string name="read_storage_permission_rationale">Szükséges engedély: Külső tárhely olvasása. Az alkalmazás nem működik enélkül.</string>
<string name="location_permission_rationale">Lehetséges engedély: Jelenlegi hely megszerzése, a kategóriajavaslatok lehetőségéért.</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Közeli helyek</string>

View file

@ -133,7 +133,8 @@
<string name="detail_description_empty">אין תיאור</string>
<string name="detail_license_empty">רישיון לא ידוע</string>
<string name="menu_refresh">רענון</string>
<string name="storage_permission_rationale">הרשאה מחייבת: אחסון. היישום לא יכול לעבוד בלי זה.</string>
<string name="read_storage_permission_rationale">הרשאה מחייבת: אחסון. היישום לא יכול לעבוד בלי זה.</string>
<string name="write_storage_permission_rationale">נדרשת הרשאה: כתיבה לאחסון חיצוני. היישום לא יכול לעבוד בלי זה.</string>
<string name="location_permission_rationale">הרשאה לא מחייבת: קבלת מיקום נוכחי בשביל הצעות קטגוריות</string>
<string name="ok">אישור</string>
<string name="title_activity_nearby">מקומות בסביבה</string>
@ -190,4 +191,6 @@
<string name="error_while_cache">שגיאה במשירת תמונות במטמון</string>
<string name="title_info">כותרת מתארת ייחודית לקובץ, שתשמש שם קובץ. אפשר להשתמש בשפה פשוטה עם רווחים. אין לכלול סיומת קובץ</string>
<string name="description_info">נא לתאר את המדיה כמה שיותר: איפה היא נוצרה? מה היא מראה? מה ההקשר? נא לתאר את העצמים או את האנשים. נא לחשוף מידע שאי־אפשר לנחש בקלות, למשל, הזמן ביום אם זאת תמונת נוף. אם המדיה מציגה משהו בלתי־רגיל, נא להסביר מה מיוחד בה.</string>
<string name="use_external_storage">להשתמש באחסון חיצוני</string>
<string name="use_external_storage_summary">שמירת תמונות שצולמו באמצעות מצלמה בתוך היישום במכשיר שלך</string>
</resources>

View file

@ -125,7 +125,7 @@
<string name="detail_description_empty">説明はありません。</string>
<string name="detail_license_empty">不明なライセンス</string>
<string name="menu_refresh">更新</string>
<string name="storage_permission_rationale">必要な権限:外部ストレージを読み込みます。これがなければアプリは機能しません。</string>
<string name="read_storage_permission_rationale">必要な権限:外部ストレージを読み込みます。これがなければアプリは機能しません。</string>
<string name="location_permission_rationale">オプションの権限:カテゴリ候補の現在の位置を取得する</string>
<string name="ok">承認</string>
<string name="title_activity_nearby">周りの場所</string>
@ -138,11 +138,15 @@
<string name="media_detail_description">記述</string>
<string name="media_detail_description_explanation">ここにメディアの説明が入ります。かなり長文になる場合には数行にわたることがあります。それでも見栄えがよいと願っています。</string>
<string name="media_detail_uploaded_date">アップロード日時</string>
<string name="media_detail_license">ライセンス</string>
<string name="media_detail_coordinates">緯度経度</string>
<string name="media_detail_coordinates_empty">情報なし</string>
<string name="become_a_tester_title">ベータ版を使ってみましょう!</string>
<string name="become_a_tester_description">Google Playのベータ版チャンネルにオプトインして、新機能やバグ修正プログラムに早期にアクセス</string>
<string name="use_wikidata">ウィキデータを使用してください</string>
<string name="use_wikidata_summary">(警告:これを無効にすると、モバイルデータを大量に消費する可能性があります)</string>
<string name="number_of_uploads">最近のアップロードファイルに表示する最大件数</string>
<string name="logout_verification">ログアウトしてもよろしいですか?</string>
<string name="background_image">背景画像</string>
<string name="no_image_found">画像がありません</string>
<string name="upload_image">画像をアップロード</string>
@ -163,4 +167,5 @@
<string name="navigation_item_info">チュートリアル</string>
<string name="no_description_found">説明がありません</string>
<string name="nearby_info_menu_wikidata_article">ウィキデータ項目</string>
<string name="use_external_storage">外部ストレージを使用</string>
</resources>

View file

@ -119,7 +119,7 @@
<string name="detail_description_empty">Tanpa katerangan</string>
<string name="detail_license_empty">Lisènsi ora kaweruhan</string>
<string name="menu_refresh">Anyarana</string>
<string name="storage_permission_rationale">Butuh palilah: Maca panyimpenan njaba. Aplikasi mokal mlaku yèn tanpa iki.</string>
<string name="read_storage_permission_rationale">Butuh palilah: Maca panyimpenan njaba. Aplikasi mokal mlaku yèn tanpa iki.</string>
<string name="location_permission_rationale">Palilah manasuka: Njupuk pernah saiki kanggo saran ing kategori</string>
<string name="ok">Oké</string>
<string name="title_activity_nearby">Papan Cedhak Kéné</string>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Ulac aglam</string>
<string name="detail_license_empty">Turagt tarussint</string>
<string name="menu_refresh">Smiren</string>
<string name="storage_permission_rationale">Yesra tasiregt: Ɣeṛ asekles azɣaray. Asnas ur yezmir ara ad yeddu s war aya.</string>
<string name="read_storage_permission_rationale">Yesra tasiregt: Ɣeṛ asekles azɣaray. Asnas ur yezmir ara ad yeddu s war aya.</string>
<string name="write_storage_permission_rationale">Ysera tasiregt: Aru deg usekles azɣaray. Asnas ur yezmir ara ad yeddu s war aya.</string>
<string name="location_permission_rationale">Tasiregt tafrayant: Awi adig amiran i yisumar n taggayt</string>
<string name="ok">IH</string>
<string name="title_activity_nearby">Idigen iqeṛben</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Tuccḍa di tririt n tugniwin ar tuffirt</string>
<string name="title_info">Azwul n useglem asuf i ufaylu, ara ttwasqedcen d isem n ufaylu. Tzemreḍ ad tesqedceḍ tutlayt fessusen s isekkilen ilmawen. Ur sedday ara asiɣzef n ufaylu</string>
<string name="description_info">Ma ulac aɣilf, seglem amidya s wayen akk i tzemreḍ: Anida yettwasekles? Acu i d-yemmal? D acu-t usatal-is? Seglem tiɣawsiwin neɣ imdanen. Mudd-d talɣut ur yezmiren ad tettwaf s wudem fessusen, amedya akud n wass ma yella d agama. Ma yella admidya yaskan-d ayen ur nuɣ ara tanumi, ini-d d aci i tyettarran d ayen ifazen.</string>
<string name="use_external_storage">Seqdec asekles azɣaray</string>
<string name="use_external_storage_summary">Sekles tiwlafin yettwaṭṭfen s tkamirat yellan deg ibenk</string>
</resources>

View file

@ -133,7 +133,8 @@
<string name="detail_description_empty">설명 없음</string>
<string name="detail_license_empty">알 수 없는 라이선스</string>
<string name="menu_refresh">새로 고침</string>
<string name="storage_permission_rationale">권한 필요: 외부 저장소 읽기. 이것이 없으면 앱은 동작하지 않습니다.</string>
<string name="read_storage_permission_rationale">권한 필요: 외부 저장소 읽기. 이것이 없으면 앱은 동작하지 않습니다.</string>
<string name="write_storage_permission_rationale">권한 필요: 외부 저장소 쓰기. 이것이 없으면 앱은 동작하지 않습니다.</string>
<string name="location_permission_rationale">선택적 권한: 분류 추천을 위해 현재 위치 정보를 가져옵니다.</string>
<string name="ok">확인</string>
<string name="title_activity_nearby">근처의 장소</string>
@ -191,4 +192,6 @@
<string name="nearby_info_menu_commons_article">공용 파일 문서</string>
<string name="nearby_info_menu_wikidata_article">위키데이터 항목</string>
<string name="error_while_cache">그림 캐시 처리 오류</string>
<string name="use_external_storage">외부 저장소 사용하기</string>
<string name="use_external_storage_summary">장치의 인앱 카메라로 찍은 사진 저장하기</string>
</resources>

View file

@ -132,7 +132,7 @@
<string name="detail_description_empty">Keng Beschreiwung</string>
<string name="detail_license_empty">Onbekannt Lizenz</string>
<string name="menu_refresh">Aktualiséieren</string>
<string name="storage_permission_rationale">Obligatoresch Autorisatioun: Externe Späicher liesen. D\'App kann net ouni dat funktionéieren.</string>
<string name="read_storage_permission_rationale">Obligatoresch Autorisatioun: Externe Späicher liesen. D\'App kann net ouni dat funktionéieren.</string>
<string name="location_permission_rationale">Fakultativ Autorisatioun: Déi aktuell Plaz kréie fir Propose fir Kategorien</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Plazen nobäi</string>
@ -185,4 +185,6 @@
<string name="no_description_found">keng Beschreiwung fonnt</string>
<string name="nearby_info_menu_commons_article">Commons-Fichierssäit</string>
<string name="nearby_info_menu_wikidata_article">Wikidata-Element</string>
<string name="use_external_storage">Externe Späicher benotzen</string>
<string name="use_external_storage_summary">Biller späicheren déi mat der in-app Kamera vun Ärem Apparat gemaach goufen</string>
</resources>

View file

@ -106,7 +106,7 @@
<string name="detail_description_empty">Nėra aprašymo</string>
<string name="detail_license_empty">Nežinoma licencija</string>
<string name="menu_refresh">Atnaujinti</string>
<string name="storage_permission_rationale">Reikalinga teisė: Skaityti išorinę talpyklą. Programėle be to negali funkcionuoti.</string>
<string name="read_storage_permission_rationale">Reikalinga teisė: Skaityti išorinę talpyklą. Programėle be to negali funkcionuoti.</string>
<string name="location_permission_rationale">Neprivaloma teisė: Gauti dabartinę vietovę, kad būtų pasiūlomos kategorijos</string>
<string name="ok">Gerai</string>
<string name="title_activity_nearby">Netoliese Esančios Vietos</string>

View file

@ -55,6 +55,11 @@
<string name="preference_license">Licence</string>
<string name="preference_theme">Nakts režīms</string>
<string name="license_name_cc0">CC0</string>
<string name="license_name_cc_by_sa_3_0">CC BY-SA 3.0</string>
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
<string name="license_name_cc_by_sa_4_0">CC BY-SA 4.0</string>
<string name="license_name_cc_by_4_0">CC BY 4.0</string>
<string name="license_name_cc_zero">CC Zero</string>
<string name="welcome_final_text">Jums šķiet, ka sapratāt?</string>
<string name="welcome_final_button_text">Jā!</string>
<string name="detail_panel_cats_label">Kategorijas</string>
@ -67,6 +72,7 @@
<string name="yes"></string>
<string name="no"></string>
<string name="media_detail_title">Nosaukums</string>
<string name="media_detail_description">Apraksts</string>
<string name="media_detail_uploaded_date">Augšupielādēšanas datums</string>
<string name="media_detail_license">Licence</string>
<string name="media_detail_coordinates">Koordinātas</string>
@ -75,6 +81,7 @@
<string name="upload_image">Augšupielādēt attēlu</string>
<string name="welcome_image_llamas">Lama</string>
<string name="welcome_image_tulip">Tulpe</string>
<string name="welcome_image_no_selfies">Nekādu pašbilžu</string>
<string name="cancel">Atcelt</string>
<string name="navigation_drawer_open">Atvērt</string>
<string name="navigation_drawer_close">Aizvērt</string>
@ -85,6 +92,7 @@
<string name="navigation_item_settings">Iestatījumi</string>
<string name="navigation_item_feedback">Atsauksmes</string>
<string name="navigation_item_logout">Iziet</string>
<string name="navigation_item_info" fuzzy="true">Ievads</string>
<string name="navigation_item_info">Apmācība</string>
<string name="no_description_found">apraksts nav atrasts</string>
<string name="use_external_storage">Izmantot ārējo krātuvi</string>
</resources>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Нема опис</string>
<string name="detail_license_empty">Непозната лиценца</string>
<string name="menu_refresh">Превчитај</string>
<string name="storage_permission_rationale">Потребна дозвола: Треба да се прочита од надворешен склад. Прилогот не може да работи без ова.</string>
<string name="read_storage_permission_rationale">Потребна дозвола: Треба да се прочита од надворешен склад. Прилогот не може да работи без ова.</string>
<string name="write_storage_permission_rationale">Потребна дозвола: Треба да се запише на надворешен склад. Прилогот не може да работи без ова.</string>
<string name="location_permission_rationale">Дозвола по желба: Утврдување на тековната местоположба за предлагање категории</string>
<string name="ok">ОК</string>
<string name="title_activity_nearby">Околни места</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Грешка при меѓускладирање на сликите</string>
<string name="title_info">Краток и единствен наслов на податотеката, кој ќе служи како нејзин назив. Можете да користите прост јазик со меѓупростор, но не пишувајте ја податотечната наставка</string>
<string name="description_info">Објаснете ја податотеката што подобро можете: Каде е направена? Што е прикажано на неа? Кој е контекстот? Опишете ги предметите, објектите и личностите. Дајте сознанија што не можат лесно да се погодат, како на пр. време од денот ако се работи за природен предел. Ако на неа е претставено нешто необично, објаснете зошто прикажаното е необично.</string>
<string name="use_external_storage">Користи надворешен склад</string>
<string name="use_external_storage_summary">Зачувување на направените слики во прилогот со камерата на вашиот уред</string>
</resources>

View file

@ -24,7 +24,7 @@
<item quantity="one">१ संचिका अपभारीत आहे</item>
<item quantity="other">%d संचिका अपभारीत आहे</item>
</plurals>
<string name="title_activity_contributions" fuzzy="true">माझी अपभारणे</string>
<string name="title_activity_contributions">माझी अपभारणे</string>
<string name="contribution_state_queued">प्रतिक्षावलीत ठेवले</string>
<string name="contribution_state_failed">अपयशी</string>
<string name="contribution_state_in_progress">%1$d%% पूर्ण</string>
@ -42,6 +42,7 @@
<string name="login_failed_password">सनोंद प्रवेश अशक्य - कृपया आपला परवलीचा शब्द तपासा</string>
<string name="login_failed_throttled">अनेक अयशस्वी प्रयत्न.काही मिनीटांनंतर पुन्हा प्रयत्न करा.</string>
<string name="login_failed_blocked">माफ करा,कॉमन्सवर हा सदस्य प्रतिबंधित आहे</string>
<string name="login_failed_2fa_needed">आपण आपल्या दोन कारक प्रमाणिकरण कोड प्रदान करणे आवश्यक आहे.</string>
<string name="login_failed_generic">सनोंद प्रवेश अयशस्वी!</string>
<string name="share_upload_button">अपभारण करा</string>
<string name="multiple_share_base_title">या संचास नाव द्या</string>
@ -117,7 +118,7 @@
<string name="detail_description_empty">वर्णन नाही.</string>
<string name="detail_license_empty">अनोळखी परवाना</string>
<string name="menu_refresh">ताजेतवाने करा</string>
<string name="storage_permission_rationale">परवानगी आवश्यक:बाह्य भंडारण वाचन. याशिवाय अॲप काम करू शकत नाही.</string>
<string name="read_storage_permission_rationale">परवानगी आवश्यक:बाह्य भंडारण वाचन. याशिवाय अॲप काम करू शकत नाही.</string>
<string name="location_permission_rationale">ऐच्छिक परवानगी:वर्ग सुचवण्यांसाठी सध्याचे स्थान मिळवा</string>
<string name="ok">ठीक आहे</string>
<string name="title_activity_nearby">जवळपासची स्थाने</string>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Ingen beskrivelse</string>
<string name="detail_license_empty">Ukjent lisens</string>
<string name="menu_refresh">Gjenoppfrisk</string>
<string name="storage_permission_rationale">Nødvendig tillatelse: Lese ekstern lagring. Appen virker ikke uten dette.</string>
<string name="read_storage_permission_rationale">Nødvendig tillatelse: Lese ekstern lagring. Appen virker ikke uten dette.</string>
<string name="write_storage_permission_rationale">Påkrevd tillatelse: Skriv til ekstern lagring. Appen fungerer ikke uten dette.</string>
<string name="location_permission_rationale">Valgfri tillatelse: Hent nåværende posisjon for kategoriforslag</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Plasser i nærheten</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Feil under mellomlagring av bilder</string>
<string name="title_info">En unik beskrivende tittel for fila, som vil fungere som filnavn. Du kan bruke vanlig språk med mellomrom. Ikke ta med filendelsen</string>
<string name="description_info">Beskriv bidraget så mye som mulig: Hvor ble det tatt? Hva viser det? Hva er konteksten? Beskriv objektene eller personene. Gi informasjon som ikke kan gjettes lett, for eksempel når på dagen bildet ble tatt om det er et landskapsbilde. Om bildet viser noe uvanlig, forklar hva som gjør det uvanlig.</string>
<string name="use_external_storage">Bruk ekstern lagring</string>
<string name="use_external_storage_summary">Lagre bilder som er tatt med kameraet i appen på enheten din</string>
</resources>

View file

@ -129,7 +129,7 @@
<string name="detail_description_empty">Geen beschrijving</string>
<string name="detail_license_empty">Onbekende licentie</string>
<string name="menu_refresh">Vernieuwen</string>
<string name="storage_permission_rationale">Benodigde toestemming: Lees externe opslag. Zonder deze toestemming kan de app niet functioneren.</string>
<string name="read_storage_permission_rationale">Benodigde toestemming: Lees externe opslag. Zonder deze toestemming kan de app niet functioneren.</string>
<string name="location_permission_rationale">Optionele toestemming: Huidige locatie ophalen voor categoriesuggesties</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Plaatsen in de buurt</string>

View file

@ -9,6 +9,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>
@ -29,6 +30,7 @@
<string name="contribution_state_starting">ਅੱਪਲੋਡ ਜਾਰੀ ਹੈ</string>
<string name="menu_from_gallery">ਚਿੱਤਰਸ਼ਾਲਾ ਤੋਂ</string>
<string name="menu_from_camera">ਤਸਵੀਰ ਲਵੋ</string>
<string name="menu_nearby">ਨੇੜੇ-ਤੇੜੇ</string>
<string name="provider_contributions">ਮੇਰੇ ਅੱਪਲੋਡ</string>
<string name="menu_share">ਸਾਂਝਾ ਕਰੋ</string>
<string name="menu_open_in_browser">ਬਰਾਊਜ਼ਰ ਵਿਚ ਵੇਖੋ</string>
@ -37,7 +39,7 @@
<string name="login_failed_network">ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਨੈੱਟਵਰਕ ਫੇਲ੍ਹ ਹੋਇਆ ਹੈ</string>
<string name="login_failed_username">ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਆਪਣਾ ਵਰਤੋਂਕਾਰ ਨਾਂ ਚੈੱਕ ਕਰੋ</string>
<string name="login_failed_password">ਦਾਖ਼ਲਾ ਨਹੀਂ ਹੋ ਰਿਹਾ - ਆਪਣਾ ਪਾਸਵਰਡ ਚੈੱਕ ਕਰੋ ਜੀ</string>
<string name="login_failed_throttled" fuzzy="true">ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ</string>
<string name="login_failed_throttled">ਬਹੁਤ ਸਾਰੀਆਂ ਅਸਫ਼ਲ ਕੋਸ਼ਿਸ਼ਾਂ। ਥੋੜ੍ਹੀ ਦੇਰ ਬਾਅਦ ਕੋਸ਼ਿਸ਼ ਕਰੋ ਜੀ</string>
<string name="login_failed_blocked">ਅਫ਼ਸੋਸ, ਇਹ ਵਰਤੋਂਕਾਰ ਕਾਮਨਜ਼ ਉੱਤੇ ਬਲਾਕ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ</string>
<string name="login_failed_generic">ਦਾਖ਼ਲਾ ਫੇਲ੍ਹ ਹੋਇਆ</string>
<string name="share_upload_button">ਚੜ੍ਹਾਉ</string>
@ -46,39 +48,53 @@
<string name="menu_upload_single">ਅੱਪਲੋਡ</string>
<string name="categories_search_text_hint">ਸ਼੍ਰੇਣੀਆਂ ਖੋਜੋ</string>
<string name="menu_save_categories">ਸਾਂਭੋ</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">ਅਜੇ ਕੋਈ ਚੜ੍ਹਾਈ ਨਹੀਂ</item>
<item quantity="one"> ਚੜ੍ਹਾਈ</item>
<item quantity="other">%d ਚੜ੍ਹਾਈ</item>
<string name="refresh_button">ਤਾਜ਼ਾ ਕਰੋ</string>
<string name="gps_disabled">ਤੁਹਾਡੇ ਉਪਕਰਣ ਦਾ ਜੀਪੀਐੱਸ ਬੰਦ ਹੈ। ਇਸਨੂੰ ਚਲਾਉਣਾ ਚਾਹੋਂਗੇ?</string>
<string name="enable_gps">ਜੀਪੀਐੱਸ ਚਾਲੂ ਕਰੋ</string>
<string name="contributions_subtitle_zero">ਫ਼ਿਲਹਾਲ ਕੋਈ ਅੱਪਲੋਡ ਨਹੀਂ</string>
<plurals name="contributions_subtitle">
<item quantity="zero">\@string/contributions_subtitle_zero</item>
<item quantity="one">%d upload</item>
<item quantity="other">%d ਅੱਪਲੋਡ</item>
</plurals>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one"> ਚੜ੍ਹਾਈ ਸ਼ੁਰੂ ਹੋ ਰਹੀ ਹੈ</item>
<plurals name="starting_multiple_uploads">
<item quantity="one">%d ਅੱਪਲੋਡ ਸ਼ੁਰੂ ਹੋ ਰਹੀ ਹੈ</item>
<item quantity="other">%d ਸ਼ੁਰੂ ਹੋ ਰਹੇ ਹਨ</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<item quantity="one"> ਚੜ੍ਹਾਈ</item>
<item quantity="other">%d ਚੜ੍ਹਾਈਆ</item>
<plurals name="multiple_uploads_title">
<item quantity="one">&amp;d ਅੱਪਲੋਡ</item>
<item quantity="other">%d ਅੱਪਲੋਡਾ</item>
</plurals>
<string name="categories_not_found">%1$s ਨਾਲ਼ ਮੇਲ ਖਾਂਦੀ ਕੋਈ ਸ਼੍ਰੇਣੀ ਨਹੀਂ ਲੱਭੀ</string>
<string name="categories_skip_explanation">ਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਨੂੰ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਵਿਚ ਜ਼ਿਆਦਾ ਲੱਭਣਯੋਗ ਬਣਾਉਣ ਲਈ ਸ਼੍ਰੇਣੀਆਂ ਜੋੜੋ।\n\nਸ਼੍ਰੇਣੀਆਂ ਜੋੜਨ ਲਈ ਟਾਈਪ ਕਰਨ ਅਰੰਭ ਕਰੋ।\nਇਸ ਕਾਰਜ ਨੂੰ ਅਣਡਿੱਠਾ ਕਰਨ ਲਈ ਇਹ ਸੁਨੇਹਾ ਥਪੇੜੋ (ਜਾਂ ਵਾਪਸੀ ਬਟਨ ਦਬਾਓ)।</string>
<string name="categories_activity_title">ਸ਼੍ਰੇਣੀਆਂ</string>
<string name="title_activity_settings">ਸੈਟਿੰਗ</string>
<string name="title_activity_signup">ਸਾਈਨ ਅੱਪ</string>
<string name="menu_about">ਇਸ ਬਾਰੇ</string>
<string name="about_license" fuzzy="true">ਅਜ਼ਾਦ ਸਰੋਤ ਸਾਫ਼ਟਵੇਅਰ ਨੂੰ &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache License v2&lt;/a&gt; ਅਧੀਨ ਜਾਰੀ ਕੀਤਾ ਗਿਆ ਹੈ</string>
<string name="about_license">ਅਜ਼ਾਦ ਸਰੋਤ ਸਾਫ਼ਟਵੇਅਰ ਨੂੰ &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache License v2&lt;/a&gt; ਅਧੀਨ ਜਾਰੀ ਕੀਤਾ ਗਿਆ ਹੈ</string>
<string name="about_improve" fuzzy="true">ਸਰੋਤ &lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;GitHub&lt;/a&gt; ਉੱਤੇ। ਮੁਸ਼ਕਲਾਂ &lt;a href=\" https://github.com/commons-app/apps-android-commons/issues\"&gt;Github&lt;/a&gt; ਉੱਤੇ।</string>
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Privacy policy&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;ਕਰੈਡਿਟਸ&lt;/a&gt;</string>
<string name="title_activity_about">ਇਸ ਬਾਰੇ</string>
<string name="menu_feedback">ਵਿਚਾਰ ਭੇਜੋ (ਈਮੇਲ ਰਾਹੀਂ)</string>
<string name="no_email_client">ਕੋਈ ਈਮੇਲ ਸਾਧਨ ਇੰਸਟਾਲ ਨਹੀਂ ਕੀਤਾ ਗਿਆ</string>
<string name="provider_categories">ਹਾਲ \'ਚ ਵਰਤੀਆਂ ਗਈਆਂ ਸ਼੍ਰੇਣੀਆਂ</string>
<string name="waiting_first_sync">ਪਹਿਲੀ ਸਿੰਕ ਲਈ ਉਡੀਕ…</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$s ਹੇਠ ਲਸੰਸ ਜਾਰੀ ਕੀਤੀ ਜਾਵੇਗਾ</string>
<string name="media_upload_policy">ਇਹ ਤਸਵੀਰ ਅੱਪਲੋਡ ਕਰਨ ਨਾਲ ਹੀ ਮੈਂ ਦਾਅਵਾ ਕਰਦਾ ਹਾਂ/ਕਰਦੀ ਹਾਂ ਕਿ ਇਹ ਮੇਰਾ ਆਪਣਾ ਕਾਰਜ ਹੈ, ਕਿ ਇਸ ਤਹਿਤ ਕੋਈ ਕਾਪੀਰਾਈਟ ਉਲੰਘਣਾ ਨਹੀਂ ਕੀਤੀ ਗਈ ਅਤੇ &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਨੀਤੀਆਂ&lt;/a&gt; ਮੁਤਾਬਿਕ ਇਹ ਠੀਕ ਹੈ।</string>
<string name="menu_download">ਡਾਊਨਲੋਡ</string>
<string name="preference_license">ਲਸੰਸ</string>
<string name="license_name_cc_by_sa" fuzzy="true">CC Attribution-ShareAlike 3.0</string>
<string name="license_name_cc_by" fuzzy="true">CC Attribution 3.0</string>
<string name="use_previous">ਪਹਿਲਾਂ ਵਾਲਾ ਸਿਰਲੇਖ/ਜਾਣਕਾਰੀ ਵਰਤੋ</string>
<string name="allow_gps">ਆਟੋਮੈਟਿਕ ਮੌਜੂਦਾ ਸਥਿਤੀ ਦਾ ਪਤਾ</string>
<string name="preference_theme">ਰਾਤ ਦਾ ਅੰਦਾਜ਼</string>
<string name="preference_theme_summary">ਧੁੰਦਲੀ ਥੀਮ ਵਰਤੋ</string>
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
<string name="license_name_cc_by_four"> Attribution 4.0</string>
<string name="license_name_cc_by_sa">CC Attribution-ShareAlike 3.0</string>
<string name="license_name_cc_by">Attribution 3.0</string>
<string name="license_name_cc0">CC0</string>
<string name="license_name_cc_by_sa_3_0">CC BY-SA 3.0</string>
<string name="license_name_cc_by_sa_3_0_at">CC BY-SA 3.0 (ਅਸਟਰੀਆ)</string>
@ -92,7 +108,17 @@
<string name="license_name_cc_by_sa_3_0_pl">CC BY-SA 3.0 (ਪੋਲੈਂਡ)</string>
<string name="license_name_cc_by_sa_3_0_ro">CC BY-SA 3.0 (ਰੋਮਾਨੀਆ)</string>
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
<string name="license_name_cc_by_sa_4_0">CC BY-SA 4.0</string>
<string name="license_name_cc_by_4_0">CC BY 4.0</string>
<string name="license_name_cc_zero">CC ਸਿਫ਼ਰ</string>
<string name="tutorial_1_text">ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਜ਼ਿਆਦਾਤਰ ਉਹ ਤਸਵੀਰਾਂ ਦਾ ਭੰਡਾਰ ਹੈ ਜੋ ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਵਰਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ।</string>
<string name="tutorial_1_subtext">ਤੁਹਾਡੀਆਂ ਤਸਵੀਰਾਂ ਵਿਸ਼ਵ ਦੇ ਬਾਕੀ ਲੋਕਾਂ ਨੂੰ ਸਿੱਖਿਅਤ ਕਰਨ ਲਈ ਸਹਾਈ ਹਨ!</string>
<string name="tutorial_2_text">ਕਿਰਪਾ ਕਰਕੇ ਉਹ ਤਸਵੀਰਾਂ ਅਪਲੋਡ ਕਰੋ ਜੋ ਤੁਹਾਡੇ ਦੁਆਰਾ ਲਈਆਂ ਗਈਆਂ ਹਨ ਜਾਂ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ:</string>
<string name="tutorial_2_subtext">- ਕੁਦਰਤੀ ਇਕਾਈ (ਫੁੱਲ, ਜਾਨਵਰ, ਪਰਬਤ)\n- ਲਾਹੇਵੰਦ ਇਕਾਈਆਂ (ਸਾਇਕਲ, ਰੇਲਵੇ ਸਟੇਸ਼ਨ)\n- ਪ੍ਰਸਿੱਧ ਲੋਕ (ਤੁਹਾਡਾ ਮੇਅਰ, ਅਥਲੀਟ)</string>
<string name="tutorial_3_text">ਕਿਰਪਾ ਕਰਕੇ ਅਪਲੋਡ ਨਾ ਕਰੋ:</string>
<string name="tutorial_3_subtext">- ਸੈਲਫ਼ੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਦੋਸਤਾਂ ਦੀਆਂ ਤਸਵੀਰਾਂ\n- ਜੋ ਤਸਵੀਰਾਂ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਤੋਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਹਨ\n- ਨਿੱਜੀ ਐਪਲੀਕੇਸ਼ਨਾਂ ਦੇ ਸਕਰੀਨਸ਼ੌਟ</string>
<string name="tutorial_4_text">ਉਦਾਹਰਣ ਵਜੋਂ ਇਹ ਅਪਲੋਡ:</string>
<string name="tutorial_4_subtext">- ਸਿਰਲੇਖ: Sydney Opera House\n- ਵੇਰਵਾ: ਸਿਡਨੀ ਓਪੇਰਾ ਹਾਊਸ ਦੀ ਤਸਵੀਰ\n- ਸ਼੍ਰੇਣੀਆਂ: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views</string>
<string name="welcome_wikipedia_text">ਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਦਾ ਯੋਗਦਾਨ ਪਾਓ। ਵਿਕੀਪੀਡੀਆ ਲੇਖਾਂ ਨੂੰ ਸੁਰਜੀਤ ਕਰ ਦਿਓ!</string>
<string name="welcome_wikipedia_subtext">ਵਿਕੀਪੀਡੀਆ ਉਤਲੀਆਂ ਤਸਵੀਰਾਂ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਤੋਂ ਆਉਂਦੀਆਂ ਹਨ</string>
<string name="welcome_copyright_text">ਤੁਹਾਡੀਆਂ ਤਸਵੀਰਾਂ ਦੁਨੀਆਂ ਭਰ ਦੇ ਲੋਕਾਂ ਨੂੰ ਪੜ੍ਹਨ ਵਿਚ ਮਦਦ ਕਰਦੀਆਂ ਹਨ।</string>
@ -100,9 +126,46 @@
<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" fuzzy="true">ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ</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="read_storage_permission_rationale">ਆਗਿਆ ਚਾਹੀਦੀ ਹੈ: ਬਾਹਰੀ ਸਟੋਰੇਜ ਬਾਰੇ। ਇਸ ਤੋਂ ਬਿਨਾਂ ਐਪ ਕਾਰਜ ਨਹੀਂ ਕਰ ਸਕੇਗੀ।</string>
<string name="ok">ਠੀਕ ਹੈ</string>
<string name="title_activity_nearby">ਨਜ਼ਦੀਕੀ ਥਾਵਾਂ</string>
<string name="no_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="media_detail_uploaded_date">ਅਪਲੋਡ ਦੀ ਮਿਤੀ</string>
<string name="media_detail_license">ਲਸੰਸ</string>
<string name="media_detail_coordinates">ਗੁਣਕ</string>
<string name="media_detail_coordinates_empty">ਕੋਈ ਉਪਲਬਧ ਨਹੀਂ</string>
<string name="use_wikidata">ਵਿਕੀਡਾਟਾ ਵਰਤੋ</string>
<string name="_2fa_code">2FA ਕੋਡ</string>
<string name="maximum_limit">ਵੱਧ ਤੋਂ ਵੱਧ ਹੱਦ</string>
<string name="maximum_limit_alert">500 ਤੋਂ ਵੱਧ ਵਿਖਾਉਣ ਵਿੱਚ ਅਸਮਰੱਥ</string>
<string name="logout_verification">ਕੀ ਤੁਸੀਂ ਸੱਚੀਂ ਬੰਦ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?</string>
<string name="commons_logo">ਕਾਮਨਜ਼ ਲੋਗੋ</string>
<string name="background_image">ਪਿੱਠਵਰਤੀ ਤਸਵੀਰ</string>
<string name="upload_image">ਤਸਵੀਰ ਅਪਲੋਡ</string>
<string name="welcome_image_no_selfies">ਕੋਈ ਸੈਲਫ਼ੀਆਂ ਨਹੀਂ</string>
<string name="welcome_image_welcome_wikipedia">ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਸੁਆਗਤ</string>
<string name="welcome_image_welcome_copyright">ਕਾਪੀਰਾਈਟ ਸੁਆਗਤ</string>
<string name="cancel">ਰੱਦ ਕਰੋ</string>
<string name="navigation_drawer_open">ਖੋਲ੍ਹੋ</string>
<string name="navigation_drawer_close">ਬੰਦ ਕਰੋ</string>
<string name="navigation_item_home">ਮੁੱਖ ਸਫ਼ਾ</string>
<string name="navigation_item_upload">ਚੜ੍ਹਾਉ</string>
<string name="navigation_item_nearby">ਨੇੜੇ-ਤੇੜੇ</string>
<string name="navigation_item_about">ਬਾਰੇ</string>
<string name="navigation_item_settings">ਸੈਟਿੰਗਾਂ</string>
<string name="navigation_item_feedback">ਫ਼ੀਡਬੈਕ</string>
<string name="navigation_item_logout">ਬਾਹਰ ਆਉ</string>
<string name="navigation_item_info">ਟਿਊਟੋਰਿਅਲ</string>
</resources>

View file

@ -42,6 +42,7 @@
<string name="login_failed_password">Nie można zalogować - sprawdź hasło</string>
<string name="login_failed_throttled">Zbyt wiele nieudanych prób zalogowania. Spróbuj ponownie za kilka minut.</string>
<string name="login_failed_blocked">Przepraszamy, ten użytkownik został zablokowany na Commons</string>
<string name="login_failed_2fa_needed">Wprowadź swój kod dla dwuetapowej autoryzacji.</string>
<string name="login_failed_generic">Logowanie nie powiodło się</string>
<string name="share_upload_button">Prześlij</string>
<string name="multiple_share_base_title">Nazwij ten zestaw</string>
@ -52,6 +53,7 @@
<string name="refresh_button">Odśwież</string>
<string name="gps_disabled">GPS w twoim urządzeniu jest wyłączony. Czy chcesz go włączyć?</string>
<string name="enable_gps">Włącz GPS</string>
<string name="contributions_subtitle_zero">Na razie brak przesłanych plików!</string>
<plurals name="contributions_subtitle">
<item quantity="zero">Przesłano @string/contributions_subtitle_zero</item>
<item quantity="one">Przesłano %d plik</item>
@ -131,7 +133,7 @@
<string name="detail_description_empty">Brak opisu</string>
<string name="detail_license_empty">Nieznana licencja</string>
<string name="menu_refresh">Odśwież</string>
<string name="storage_permission_rationale">Wymagane uprawnienia: odczyt z dysku zewnętrznego. Aplikacja nie będzie w stanie funkcjonować bez tego.</string>
<string name="read_storage_permission_rationale">Wymagane uprawnienia: odczyt z dysku zewnętrznego. Aplikacja nie będzie w stanie funkcjonować bez tego.</string>
<string name="location_permission_rationale">Opcjonalne zezwolenie: uzyskiwanie bieżącej lokalizacji dla wygenerowania propozycji kategorii</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Pobliskie miejsca</string>
@ -151,6 +153,8 @@
<string name="become_a_tester_description">Dołącz do kanału bety w Google Play i dostań wczesny dostęp do nowych funkcji i łatek</string>
<string name="use_wikidata">Użyj Wikidanych</string>
<string name="_2fa_code">Kod 2FA</string>
<string name="maximum_limit">Górna Granica</string>
<string name="maximum_limit_alert">Nie można wyświetlić ponad 500</string>
<string name="login_failed_2fa_not_supported">Uwierzytelnianie dwuskładnikowe obecnie nie jest obsługiwane.</string>
<string name="logout_verification">Czy na pewno wylogować?</string>
<string name="commons_logo">Logo Commons</string>
@ -160,8 +164,10 @@
<string name="welcome_image_llamas">Lamy</string>
<string name="welcome_image_rainbow_bridge">Rainbow Bridge</string>
<string name="welcome_image_tulip">Tulipan</string>
<string name="welcome_image_no_selfies">Brak Selfie</string>
<string name="welcome_image_welcome_wikipedia">Witaj na Wikipedii</string>
<string name="welcome_image_welcome_copyright" fuzzy="true">Witaj w prawach autorskich.</string>
<string name="welcome_image_sydney_opera_house">Opera Sydnej</string>
<string name="cancel">Anuluj</string>
<string name="navigation_drawer_open">Otwórz</string>
<string name="navigation_drawer_close">Zamknij</string>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Gnun-a descrission</string>
<string name="detail_license_empty">Licensa sconossùa</string>
<string name="menu_refresh">Rinfrësché</string>
<string name="storage_permission_rationale">Autorisassion necessaria: Lese n\'anmagasinament estern. L\'aplicassion a peul pa marcé sensa lòn.</string>
<string name="read_storage_permission_rationale">Autorisassion necessaria: Lese n\'anmagasinament estern. L\'aplicassion a peul pa marcé sensa lòn.</string>
<string name="write_storage_permission_rationale">Autorisassion necessaria: Scrive n\'anmagasinament estern. L\'aplicassion a peul pa marcé sensa \'d lòn.</string>
<string name="location_permission_rationale">Autorisassion facoltativa: Oten-e la posission atual për dij sugeriment ëd categorìa</string>
<string name="ok">Va bin</string>
<string name="title_activity_nearby">Pòst davzin</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Eror antramentre ch\'as butavo le plance an memòria local</string>
<string name="title_info">Un tìtol dëscritiv ùnich për l\'archivi, che a servirà com nòm d\'archivi. A peul dovré un lengagi sempi con djë spassi. Ch\'a ancluda pa l\'estension dl\'archivi</string>
<string name="description_info">Për piasì, ch\'a descriva ël mojen mej ch\'a peul: Andoa a l\'é stàit fàit? Për piasì, ch\'a descriva j\'oget o le përson-e. Ch\'a arvela j\'anformassion ch\'a l\'é nen belfé andviné, për esempi l\'ora dël dì, s\'a l\'é un panorama. Si ël mojen a smon cheicòs ëd foravìa, për piasì ch\'a spiega lòn ch\'a lo rend foravìa.</string>
<string name="use_external_storage">Dovré n\'anmagasinament estern</string>
<string name="use_external_storage_summary">Argistré le plance pijà con la màchina fòto ëd sò angign</string>
</resources>

View file

@ -134,7 +134,8 @@
<string name="detail_description_empty">Sem descrição</string>
<string name="detail_license_empty">Licença desconhecida</string>
<string name="menu_refresh">Atualizar</string>
<string name="storage_permission_rationale">Permissão necessária: Ler armazenamento externo. O aplicativo não pode funcionar sem isso.</string>
<string name="read_storage_permission_rationale">Permissão necessária: Ler armazenamento externo. O aplicativo não pode funcionar sem isso.</string>
<string name="write_storage_permission_rationale">Permissão necessária: Escreva armazenamento externo. A aplicação não pode funcionar sem isso.</string>
<string name="location_permission_rationale">Permissão opcional: Obter a localização atual de sugestões de categoria</string>
<string name="ok">OK</string>
<string name="title_activity_nearby">Lugares próximos</string>
@ -194,4 +195,6 @@
<string name="error_while_cache">Erro durante o cache de imagens</string>
<string name="title_info">Um título descritivo exclusivo para o arquivo, que servirá como um nome de arquivo. Você pode usar linguagem simples com espaços. Não inclua a extensão do arquivo</string>
<string name="description_info">Por favor, descreva a mídia tanto quanto possível: onde foi tomada? O que isso mostra? Qual é o contexto? Descreva os objetos ou pessoas. Revelar informações que não podem ser facilmente adivinhadas, por exemplo, a hora do dia, se for uma paisagem. Se a mídia mostrar algo incomum, explique o que torna incomum.</string>
<string name="use_external_storage">Usar o armazenamento externo</string>
<string name="use_external_storage_summary">Salvar as fotos tiradas com a câmera no aplicativo no seu dispositivo</string>
</resources>

Some files were not shown because too many files have changed in this diff Show more