mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Merge branch 'main' into kn/bug/6330-NPE-LatLng-getLatitude
This commit is contained in:
commit
a47a9e7960
135 changed files with 1786 additions and 1408 deletions
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
|
@ -1,7 +1,7 @@
|
|||
name: "\U0001F41E Bug report"
|
||||
description: Create a report to help us improve.
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
type: Bug # Retained to categorize the issue as per organization-level type
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
|
|
|||
1
.idea/codeStyles/Project.xml
generated
1
.idea/codeStyles/Project.xml
generated
|
|
@ -16,6 +16,7 @@
|
|||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
|
||||
<option name="IMPORT_LAYOUT_TABLE">
|
||||
<value>
|
||||
<package name="" withSubpackages="true" static="false" module="true" />
|
||||
<package name="" withSubpackages="true" static="true" />
|
||||
<emptyLine />
|
||||
<package name="" withSubpackages="true" static="false" />
|
||||
|
|
|
|||
10
CHANGELOG.md
10
CHANGELOG.md
|
|
@ -1,5 +1,15 @@
|
|||
# Wikimedia Commons for Android
|
||||
|
||||
## v5.5.0
|
||||
|
||||
### What's changed
|
||||
* Explore images will now be shown based on the map location and not at your current location
|
||||
* Enhanced Wikidata feedback message
|
||||
* Green labels in Explore map will no longer be hidden by other pins thumbnails
|
||||
* Upload wizard's language drop-down now reflects the language used in the pin label
|
||||
* Users can now pick only one image at a time while using the custom selector
|
||||
* Bug fixes and stability improvements
|
||||
|
||||
## v5.4.1
|
||||
|
||||
### What's changed
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ android {
|
|||
applicationId = "fr.free.nrw.commons"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 1052
|
||||
versionName = "5.4.1"
|
||||
versionCode = 1053
|
||||
versionName = "5.5.0"
|
||||
|
||||
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
|
@ -347,6 +347,7 @@ dependencies {
|
|||
// Kotlin + coroutines
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
implementation(libs.androidx.work.runtime)
|
||||
implementation(libs.kotlinx.coroutines.rx2)
|
||||
testImplementation(libs.androidx.work.testing)
|
||||
|
||||
//Glide
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@
|
|||
# Application classes that will be serialized/deserialized over Gson
|
||||
-keep class com.google.gson.examples.android.model.** { *; }
|
||||
|
||||
# Prevent R8 from obfuscating project classes used by Gson for parsing
|
||||
-keep class fr.free.nrw.commons.fileusages.** { *; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.Intent.ACTION_VIEW
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
|
|
@ -16,6 +18,9 @@ import fr.free.nrw.commons.theme.BaseActivity
|
|||
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import java.util.Collections
|
||||
import androidx.core.net.toUri
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.setUnderlinedText
|
||||
|
||||
/**
|
||||
* Represents about screen of this app
|
||||
|
|
@ -59,30 +64,12 @@ class AboutActivity : BaseActivity() {
|
|||
binding!!.aboutImprove.setHtmlText(improveText)
|
||||
binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha()
|
||||
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutFaq, R.string.about_faq,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutRateUs, R.string.about_rate_us,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutUserGuide, R.string.user_guide,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutPrivacyPolicy, R.string.about_privacy_policy,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutTranslate, R.string.about_translate,
|
||||
applicationContext
|
||||
)
|
||||
Utils.setUnderlinedText(
|
||||
binding!!.aboutCredits, R.string.about_credits,
|
||||
applicationContext
|
||||
)
|
||||
binding!!.aboutFaq.setUnderlinedText(R.string.about_faq)
|
||||
binding!!.aboutRateUs.setUnderlinedText(R.string.about_rate_us)
|
||||
binding!!.aboutUserGuide.setUnderlinedText(R.string.user_guide)
|
||||
binding!!.aboutPrivacyPolicy.setUnderlinedText(R.string.about_privacy_policy)
|
||||
binding!!.aboutTranslate.setUnderlinedText(R.string.about_translate)
|
||||
binding!!.aboutCredits.setUnderlinedText(R.string.about_credits)
|
||||
|
||||
/*
|
||||
To set listeners, we can create a separate method and use lambda syntax.
|
||||
|
|
@ -106,47 +93,56 @@ class AboutActivity : BaseActivity() {
|
|||
fun launchFacebook(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL))
|
||||
intent = Intent(ACTION_VIEW, Urls.FACEBOOK_APP_URL.toUri())
|
||||
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL))
|
||||
handleWebUrl(this, Urls.FACEBOOK_WEB_URL.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchGithub(view: View?) {
|
||||
val intent: Intent
|
||||
try {
|
||||
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
intent = Intent(ACTION_VIEW, Urls.GITHUB_REPO_URL.toUri())
|
||||
intent.setPackage(Urls.GITHUB_PACKAGE_NAME)
|
||||
startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL))
|
||||
handleWebUrl(this, Urls.GITHUB_REPO_URL.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchWebsite(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL))
|
||||
handleWebUrl(this, Urls.WEBSITE_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchRatings(view: View?) {
|
||||
Utils.rateApp(this)
|
||||
try {
|
||||
startActivity(
|
||||
Intent(
|
||||
ACTION_VIEW,
|
||||
(Urls.PLAY_STORE_PREFIX + packageName).toUri()
|
||||
)
|
||||
)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
handleWebUrl(this, (Urls.PLAY_STORE_URL_PREFIX + packageName).toUri())
|
||||
}
|
||||
}
|
||||
|
||||
fun launchCredits(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL))
|
||||
handleWebUrl(this, Urls.CREDITS_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchUserGuide(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL))
|
||||
handleWebUrl(this, Urls.USER_GUIDE_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchPrivacyPolicy(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
handleWebUrl(this, BuildConfig.PRIVACY_POLICY_URL.toUri())
|
||||
}
|
||||
|
||||
fun launchFrequentlyAskedQuesions(view: View?) {
|
||||
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL))
|
||||
handleWebUrl(this, Urls.FAQ_URL.toUri())
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
|
|
@ -193,7 +189,7 @@ class AboutActivity : BaseActivity() {
|
|||
|
||||
val positiveButtonRunnable = Runnable {
|
||||
val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition]
|
||||
Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode))
|
||||
handleWebUrl(this@AboutActivity, (Urls.TRANSLATE_WIKI_URL + langCode).toUri())
|
||||
}
|
||||
showAlertDialog(
|
||||
this,
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Base presenter, enforcing contracts to atach and detach view
|
||||
*/
|
||||
public interface BasePresenter<T> {
|
||||
/**
|
||||
* Until a view is attached, it is open to listen events from the presenter
|
||||
*/
|
||||
void onAttachView(@NonNull T view);
|
||||
|
||||
/**
|
||||
* Detaching a view makes sure that the view no more receives events from the presenter
|
||||
*/
|
||||
void onDetachView();
|
||||
}
|
||||
10
app/src/main/java/fr/free/nrw/commons/BasePresenter.kt
Normal file
10
app/src/main/java/fr/free/nrw/commons/BasePresenter.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
/**
|
||||
* Base presenter, enforcing contracts to attach and detach view
|
||||
*/
|
||||
interface BasePresenter<T> {
|
||||
fun onAttachView(view: T)
|
||||
|
||||
fun onDetachView()
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* represents Licence object
|
||||
*/
|
||||
public class License {
|
||||
private String key;
|
||||
private String template;
|
||||
private String url;
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of License.
|
||||
*
|
||||
* @param key license key
|
||||
* @param template license template
|
||||
* @param url license URL
|
||||
* @param name licence name
|
||||
*
|
||||
* @throws RuntimeException if License.key or Licence.template is null
|
||||
*/
|
||||
public License(String key, String template, String url, String name) {
|
||||
if (key == null) {
|
||||
throw new RuntimeException("License.key must not be null");
|
||||
}
|
||||
if (template == null) {
|
||||
throw new RuntimeException("License.template must not be null");
|
||||
}
|
||||
this.key = key;
|
||||
this.template = template;
|
||||
this.url = url;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license key.
|
||||
* @return license key as a String.
|
||||
*/
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license template.
|
||||
* @return license template as a String.
|
||||
*/
|
||||
public String getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license name. If name is null, return license key.
|
||||
* @return license name as string. if name null, license key as String
|
||||
*/
|
||||
public String getName() {
|
||||
if (name == null) {
|
||||
// hack
|
||||
return getKey();
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the license URL
|
||||
*
|
||||
* @param language license language
|
||||
* @return URL
|
||||
*/
|
||||
public @Nullable String getUrl(String language) {
|
||||
if (url == null) {
|
||||
return null;
|
||||
} else {
|
||||
return url.replace("$lang", language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class MapController {
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
public class NearbyPlacesInfo {
|
||||
public List<Place> placeList; // List of nearby places
|
||||
public LatLng[] boundaryCoordinates; // Corners of nearby area
|
||||
public LatLng currentLatLng; // Current location when this places are populated
|
||||
public LatLng searchLatLng; // Search location for finding this places
|
||||
public List<Media> mediaList; // Search location for finding this places
|
||||
}
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
public class ExplorePlacesInfo {
|
||||
public List<Place> explorePlaceList; // List of nearby places
|
||||
public LatLng[] boundaryCoordinates; // Corners of nearby area
|
||||
public LatLng currentLatLng; // Current location when this places are populated
|
||||
public LatLng searchLatLng; // Search location for finding this places
|
||||
public List<Media> mediaList; // Search location for finding this places
|
||||
}
|
||||
}
|
||||
46
app/src/main/java/fr/free/nrw/commons/MapController.kt
Normal file
46
app/src/main/java/fr/free/nrw/commons/MapController.kt
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
|
||||
abstract class MapController {
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
inner class NearbyPlacesInfo {
|
||||
@JvmField
|
||||
var placeList: List<Place> = emptyList() // List of nearby places
|
||||
|
||||
@JvmField
|
||||
var boundaryCoordinates: Array<LatLng> = emptyArray() // Corners of nearby area
|
||||
|
||||
@JvmField
|
||||
var currentLatLng: LatLng? = null // Current location when this places are populated
|
||||
|
||||
@JvmField
|
||||
var searchLatLng: LatLng? = null // Search location for finding this places
|
||||
|
||||
@JvmField
|
||||
var mediaList: List<Media>? = null // Search location for finding this places
|
||||
}
|
||||
|
||||
/**
|
||||
* We pass this variable as a group of placeList and boundaryCoordinates
|
||||
*/
|
||||
inner class ExplorePlacesInfo {
|
||||
@JvmField
|
||||
var explorePlaceList: List<Place> = emptyList() // List of nearby places
|
||||
|
||||
@JvmField
|
||||
var boundaryCoordinates: Array<LatLng> = emptyArray() // Corners of nearby area
|
||||
|
||||
@JvmField
|
||||
var currentLatLng: LatLng? = null // Current location when this places are populated
|
||||
|
||||
@JvmField
|
||||
var searchLatLng: LatLng? = null // Search location for finding this places
|
||||
|
||||
@JvmField
|
||||
var mediaList: List<Media> = emptyList() // Search location for finding this places
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.os.Parcelable
|
||||
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
|
@ -173,7 +175,8 @@ class Media constructor(
|
|||
* Gets file page title
|
||||
* @return New media page title
|
||||
*/
|
||||
val pageTitle: PageTitle get() = Utils.getPageTitle(filename!!)
|
||||
val pageTitle: PageTitle
|
||||
get() = PageTitle(filename!!, WikiSite(COMMONS_URL))
|
||||
|
||||
/**
|
||||
* Returns wikicode to use the media file on a MediaWiki site
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
/**
|
||||
* Base interface for all the views
|
||||
*/
|
||||
public interface MvpView {
|
||||
void showMessage(String message);
|
||||
}
|
||||
|
|
@ -1,154 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level;
|
||||
import timber.log.Timber;
|
||||
|
||||
public final class OkHttpConnectionFactory {
|
||||
private static final String CACHE_DIR_NAME = "okhttp-cache";
|
||||
private static final long NET_CACHE_SIZE = 64 * 1024 * 1024;
|
||||
|
||||
public static OkHttpClient CLIENT;
|
||||
|
||||
@NonNull public static OkHttpClient getClient(final CommonsCookieJar cookieJar) {
|
||||
if (CLIENT == null) {
|
||||
CLIENT = createClient(cookieJar);
|
||||
}
|
||||
return CLIENT;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static OkHttpClient createClient(final CommonsCookieJar cookieJar) {
|
||||
return new OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.cache((CommonsApplication.getInstance()!=null) ? new Cache(new File(CommonsApplication.getInstance().getCacheDir(), CACHE_DIR_NAME), NET_CACHE_SIZE) : null)
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(getLoggingInterceptor())
|
||||
.addInterceptor(new UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(new CommonHeaderRequestInterceptor())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static HttpLoggingInterceptor getLoggingInterceptor() {
|
||||
final HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor()
|
||||
.setLevel(Level.BASIC);
|
||||
|
||||
httpLoggingInterceptor.redactHeader("Authorization");
|
||||
httpLoggingInterceptor.redactHeader("Cookie");
|
||||
|
||||
return httpLoggingInterceptor;
|
||||
}
|
||||
|
||||
private static class CommonHeaderRequestInterceptor implements Interceptor {
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Response intercept(@NonNull final Chain chain) throws IOException {
|
||||
final Request request = chain.request().newBuilder()
|
||||
.header("User-Agent", CommonsApplication.getInstance().getUserAgent())
|
||||
.build();
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnsuccessfulResponseInterceptor implements Interceptor {
|
||||
private static final String SUPPRESS_ERROR_LOG = "x-commons-suppress-error-log";
|
||||
public static final String SUPPRESS_ERROR_LOG_HEADER = SUPPRESS_ERROR_LOG+": true";
|
||||
private static final List<String> DO_NOT_INTERCEPT = Collections.singletonList(
|
||||
"api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1");
|
||||
|
||||
private static final String ERRORS_PREFIX = "{\"error";
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Response intercept(@NonNull final Chain chain) throws IOException {
|
||||
final Request rq = chain.request();
|
||||
|
||||
// If the request contains our special "suppress errors" header, make note of it
|
||||
// but don't pass that on to the server.
|
||||
final boolean suppressErrors = rq.headers().names().contains(SUPPRESS_ERROR_LOG);
|
||||
final Request request = rq.newBuilder()
|
||||
.removeHeader(SUPPRESS_ERROR_LOG)
|
||||
.build();
|
||||
|
||||
final Response rsp = chain.proceed(request);
|
||||
|
||||
// Do not intercept certain requests and let the caller handle the errors
|
||||
if(isExcludedUrl(chain.request())) {
|
||||
return rsp;
|
||||
}
|
||||
if (rsp.isSuccessful()) {
|
||||
try (final ResponseBody responseBody = rsp.peekBody(ERRORS_PREFIX.length())) {
|
||||
if (ERRORS_PREFIX.equals(responseBody.string())) {
|
||||
try (final ResponseBody body = rsp.body()) {
|
||||
throw new IOException(body.string());
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
// Log the error as debug (and therefore, "expected") or at error level
|
||||
if (suppressErrors) {
|
||||
Timber.d(e, "Suppressed (known / expected) error");
|
||||
} else {
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
return rsp;
|
||||
}
|
||||
throw new HttpStatusException(rsp);
|
||||
}
|
||||
|
||||
private boolean isExcludedUrl(final Request request) {
|
||||
final String requestUrl = request.url().toString();
|
||||
for(final String url: DO_NOT_INTERCEPT) {
|
||||
if(requestUrl.contains(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private OkHttpConnectionFactory() {
|
||||
}
|
||||
|
||||
public static class HttpStatusException extends IOException {
|
||||
private final int code;
|
||||
private final String url;
|
||||
public HttpStatusException(@NonNull Response rsp) {
|
||||
this.code = rsp.code();
|
||||
this.url = rsp.request().url().uri().toString();
|
||||
try {
|
||||
if (rsp.body() != null && rsp.body().contentType() != null
|
||||
&& rsp.body().contentType().toString().contains("json")) {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log?
|
||||
}
|
||||
}
|
||||
|
||||
public int code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
String str = "Code: " + code + ", URL: " + url;
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
122
app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt
Normal file
122
app/src/main/java/fr/free/nrw/commons/OkHttpConnectionFactory.kt
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
object OkHttpConnectionFactory {
|
||||
private const val CACHE_DIR_NAME = "okhttp-cache"
|
||||
private const val NET_CACHE_SIZE = (64 * 1024 * 1024).toLong()
|
||||
|
||||
@VisibleForTesting
|
||||
var CLIENT: OkHttpClient? = null
|
||||
|
||||
fun getClient(cookieJar: CommonsCookieJar): OkHttpClient {
|
||||
if (CLIENT == null) {
|
||||
CLIENT = createClient(cookieJar)
|
||||
}
|
||||
return CLIENT!!
|
||||
}
|
||||
|
||||
private fun createClient(cookieJar: CommonsCookieJar): OkHttpClient {
|
||||
return OkHttpClient.Builder()
|
||||
.cookieJar(cookieJar)
|
||||
.cache(
|
||||
if (CommonsApplication.instance != null) Cache(
|
||||
File(CommonsApplication.instance.cacheDir, CACHE_DIR_NAME),
|
||||
NET_CACHE_SIZE
|
||||
) else null
|
||||
)
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(HttpLoggingInterceptor().apply {
|
||||
setLevel(HttpLoggingInterceptor.Level.BASIC)
|
||||
redactHeader("Authorization")
|
||||
redactHeader("Cookie")
|
||||
})
|
||||
.addInterceptor(UnsuccessfulResponseInterceptor())
|
||||
.addInterceptor(CommonHeaderRequestInterceptor())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
private class CommonHeaderRequestInterceptor : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request().newBuilder()
|
||||
.header("User-Agent", CommonsApplication.instance.userAgent)
|
||||
.build()
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
|
||||
private const val SUPPRESS_ERROR_LOG = "x-commons-suppress-error-log"
|
||||
const val SUPPRESS_ERROR_LOG_HEADER: String = "$SUPPRESS_ERROR_LOG: true"
|
||||
|
||||
private class UnsuccessfulResponseInterceptor : Interceptor {
|
||||
@Throws(IOException::class)
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val rq = chain.request()
|
||||
|
||||
// If the request contains our special "suppress errors" header, make note of it
|
||||
// but don't pass that on to the server.
|
||||
val suppressErrors = rq.headers.names().contains(SUPPRESS_ERROR_LOG)
|
||||
val request = rq.newBuilder()
|
||||
.removeHeader(SUPPRESS_ERROR_LOG)
|
||||
.build()
|
||||
|
||||
val rsp = chain.proceed(request)
|
||||
|
||||
// Do not intercept certain requests and let the caller handle the errors
|
||||
if (isExcludedUrl(chain.request())) {
|
||||
return rsp
|
||||
}
|
||||
if (rsp.isSuccessful) {
|
||||
try {
|
||||
rsp.peekBody(ERRORS_PREFIX.length.toLong()).use { responseBody ->
|
||||
if (ERRORS_PREFIX == responseBody.string()) {
|
||||
rsp.body.use { body ->
|
||||
throw IOException(body!!.string())
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Log the error as debug (and therefore, "expected") or at error level
|
||||
if (suppressErrors) {
|
||||
Timber.d(e, "Suppressed (known / expected) error")
|
||||
} else {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
throw IOException("Unsuccessful response")
|
||||
}
|
||||
|
||||
private fun isExcludedUrl(request: Request): Boolean {
|
||||
val requestUrl = request.url.toString()
|
||||
for (url in DO_NOT_INTERCEPT) {
|
||||
if (requestUrl.contains(url)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DO_NOT_INTERCEPT = listOf(
|
||||
"api.php?format=json&formatversion=2&errorformat=plaintext&action=upload&ignorewarnings=1"
|
||||
)
|
||||
const val ERRORS_PREFIX = "{\"error"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,264 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite;
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static PageTitle getPageTitle(@NonNull String title) {
|
||||
return new PageTitle(title, new WikiSite(BuildConfig.COMMONS_URL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates licence name with given ID
|
||||
* @param license License ID
|
||||
* @return Name of license
|
||||
*/
|
||||
public static int licenseNameFor(String license) {
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return R.string.license_name_cc_by;
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return R.string.license_name_cc_by_four;
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return R.string.license_name_cc_by_sa;
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return R.string.license_name_cc_by_sa_four;
|
||||
case Prefs.Licenses.CC0:
|
||||
return R.string.license_name_cc0;
|
||||
}
|
||||
throw new IllegalStateException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates license url with given ID
|
||||
* @param license License ID
|
||||
* @return Url of license
|
||||
*/
|
||||
|
||||
|
||||
@NonNull
|
||||
public static String licenseUrlFor(String license) {
|
||||
switch (license) {
|
||||
case Prefs.Licenses.CC_BY_3:
|
||||
return "https://creativecommons.org/licenses/by/3.0/";
|
||||
case Prefs.Licenses.CC_BY_4:
|
||||
return "https://creativecommons.org/licenses/by/4.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_3:
|
||||
return "https://creativecommons.org/licenses/by-sa/3.0/";
|
||||
case Prefs.Licenses.CC_BY_SA_4:
|
||||
return "https://creativecommons.org/licenses/by-sa/4.0/";
|
||||
case Prefs.Licenses.CC0:
|
||||
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
||||
default:
|
||||
throw new IllegalStateException("Unrecognized license value: " + license);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected
|
||||
* @param title File name
|
||||
* @param extension Correct extension
|
||||
* @return File with correct extension
|
||||
*/
|
||||
public static String fixExtension(String title, String extension) {
|
||||
Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
// People are used to ".jpg" more than ".jpeg" which the system gives us.
|
||||
if (extension != null && extension.toLowerCase(Locale.ENGLISH).equals("jpeg")) {
|
||||
extension = "jpg";
|
||||
}
|
||||
title = jpegPattern.matcher(title).replaceFirst(".jpg");
|
||||
if (extension != null && !title.toLowerCase(Locale.getDefault())
|
||||
.endsWith("." + extension.toLowerCase(Locale.ENGLISH))) {
|
||||
title += "." + extension;
|
||||
}
|
||||
|
||||
// If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228)
|
||||
// If title has an extension in it, if won't be true
|
||||
if (extension == null && title.lastIndexOf(".")<=0) {
|
||||
extension = "jpg";
|
||||
title += "." + extension;
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches intent to rate app
|
||||
* @param context
|
||||
*/
|
||||
public static void rateApp(Context context) {
|
||||
final String appPackageName = context.getPackageName();
|
||||
try {
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.PLAY_STORE_PREFIX + appPackageName)));
|
||||
}
|
||||
catch (android.content.ActivityNotFoundException anfe) {
|
||||
handleWebUrl(context, Uri.parse(Urls.PLAY_STORE_URL_PREFIX + appPackageName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens Custom Tab Activity with in-app browser for the specified URL.
|
||||
* Launches intent for web URL
|
||||
* @param context
|
||||
* @param url
|
||||
*/
|
||||
public static void handleWebUrl(Context context, Uri url) {
|
||||
Timber.d("Launching web url %s", url.toString());
|
||||
|
||||
final CustomTabColorSchemeParams color = new CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor))
|
||||
.setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor))
|
||||
.build();
|
||||
|
||||
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setDefaultColorSchemeParams(color);
|
||||
builder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right);
|
||||
CustomTabsIntent customTabsIntent = builder.build();
|
||||
// Clear previous browser tasks, so that back/exit buttons work as intended.
|
||||
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
customTabsIntent.launchUrl(context, url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates. It no longer depends on google maps and any app
|
||||
* capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
*/
|
||||
public static void handleGeoCoordinates(final Context context, final LatLng latLng) {
|
||||
handleGeoCoordinates(context, latLng, 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates with specified zoom level. It no longer depends on
|
||||
* google maps and any app capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
* @param zoomLevel The zoom level
|
||||
*/
|
||||
public static void handleGeoCoordinates(final Context context, final LatLng latLng,
|
||||
final double zoomLevel) {
|
||||
final Intent mapIntent = new Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel));
|
||||
if (mapIntent.resolveActivity(context.getPackageManager()) != null) {
|
||||
context.startActivity(mapIntent);
|
||||
} else {
|
||||
ViewUtil.showShortToast(context, context.getString(R.string.map_application_missing));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To take screenshot of the screen and return it in Bitmap format
|
||||
*
|
||||
* @param view
|
||||
* @return
|
||||
*/
|
||||
public static Bitmap getScreenShot(View view) {
|
||||
View screenView = view.getRootView();
|
||||
screenView.setDrawingCacheEnabled(true);
|
||||
Bitmap drawingCache = screenView.getDrawingCache();
|
||||
if (drawingCache != null) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(drawingCache);
|
||||
screenView.setDrawingCacheEnabled(false);
|
||||
return bitmap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
*Copies the content to the clipboard
|
||||
*
|
||||
*/
|
||||
public static void copy(String label,String text, Context context){
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText(label, text);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets underlined string text to a TextView
|
||||
*
|
||||
* @param textView TextView associated with string resource
|
||||
* @param stringResourceName string resource name
|
||||
* @param context
|
||||
*/
|
||||
public static void setUnderlinedText(TextView textView, int stringResourceName, Context context) {
|
||||
SpannableString content = new SpannableString(context.getString(stringResourceName));
|
||||
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
|
||||
textView.setText(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt
|
||||
* @param date
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMonumentsEnabled(final Date date) {
|
||||
if (date.getMonth() == 8) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Util function to get the start date of wlm monument
|
||||
* For this release we are hardcoding it to be 1st September
|
||||
* @return
|
||||
*/
|
||||
public static String getWLMStartDate() {
|
||||
return "1 Sep";
|
||||
}
|
||||
|
||||
/***
|
||||
* Util function to get the end date of wlm monument
|
||||
* For this release we are hardcoding it to be 31st October
|
||||
* @return
|
||||
*/
|
||||
public static String getWLMEndDate() {
|
||||
return "30 Sep";
|
||||
}
|
||||
|
||||
/***
|
||||
* Function to get the current WLM year
|
||||
* It increments at the start of September in line with the other WLM functions
|
||||
* (No consideration of locales for now)
|
||||
* @param calendar
|
||||
* @return
|
||||
*/
|
||||
public static int getWikiLovesMonumentsYear(Calendar calendar) {
|
||||
int year = calendar.get(Calendar.YEAR);
|
||||
if (calendar.get(Calendar.MONTH) < Calendar.SEPTEMBER) {
|
||||
year -= 1;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public interface ViewHolder<T> {
|
||||
void bindModel(Context context, T model);
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentPagerAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This adapter will be used to display fragments in a ViewPager
|
||||
*/
|
||||
public class ViewPagerAdapter extends FragmentPagerAdapter {
|
||||
private List<Fragment> fragmentList = new ArrayList<>();
|
||||
private List<String> fragmentTitleList = new ArrayList<>();
|
||||
|
||||
public ViewPagerAdapter(FragmentManager manager) {
|
||||
super(manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a ViewPagerAdapter with a specified Fragment Manager and Fragment resume behavior.
|
||||
*
|
||||
* @param manager The FragmentManager
|
||||
* @param behavior An integer which represents the behavior of non visible fragments. See
|
||||
* FragmentPagerAdapter.java for options.
|
||||
*/
|
||||
public ViewPagerAdapter(FragmentManager manager, int behavior) {
|
||||
super(manager, behavior);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the fragment of the viewpager at a particular position
|
||||
* @param position
|
||||
*/
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return fragmentList.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the total number of fragments in the viewpager.
|
||||
* @return size
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return fragmentList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sets the fragment and title list in the viewpager
|
||||
* @param fragmentList List of all fragments to be displayed in the viewpager
|
||||
* @param fragmentTitleList List of all titles of the fragments
|
||||
*/
|
||||
public void setTabData(List<Fragment> fragmentList, List<String> fragmentTitleList) {
|
||||
this.fragmentList = fragmentList;
|
||||
this.fragmentTitleList = fragmentTitleList;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the title of the page at a particular position
|
||||
* @param position
|
||||
*/
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
return fragmentTitleList.get(position);
|
||||
}
|
||||
}
|
||||
44
app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.kt
Normal file
44
app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.kt
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentPagerAdapter
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* This adapter will be used to display fragments in a ViewPager
|
||||
*/
|
||||
class ViewPagerAdapter : FragmentPagerAdapter {
|
||||
private val context: Context
|
||||
private var fragmentList: List<Fragment> = emptyList()
|
||||
private var fragmentTitleList: List<String> = emptyList()
|
||||
|
||||
constructor(context: Context, manager: FragmentManager) : super(manager) {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
constructor(context: Context, manager: FragmentManager, behavior: Int) : super(manager, behavior) {
|
||||
this.context = context
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment = fragmentList[position]
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence = fragmentTitleList[position]
|
||||
|
||||
override fun getCount(): Int = fragmentList.size
|
||||
|
||||
fun setTabs(vararg titlesToFragments: Pair<Int, Fragment>) {
|
||||
// Enforce that every title must come from strings.xml and all will consistently be uppercase
|
||||
fragmentTitleList = titlesToFragments.map {
|
||||
context.getString(it.first).uppercase(Locale.ROOT)
|
||||
}
|
||||
fragmentList = titlesToFragments.map { it.second }
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Convenience method for Java callers, can be removed when everything is migrated
|
||||
@JvmStatic
|
||||
fun pairOf(first: Int, second: Fragment) = first to second
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import fr.free.nrw.commons.databinding.ActivityWelcomeBinding;
|
||||
import fr.free.nrw.commons.databinding.PopupForCopyrightBinding;
|
||||
import fr.free.nrw.commons.quiz.QuizActivity;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
|
||||
public class WelcomeActivity extends BaseActivity {
|
||||
|
||||
private ActivityWelcomeBinding binding;
|
||||
private PopupForCopyrightBinding copyrightBinding;
|
||||
|
||||
private final WelcomePagerAdapter adapter = new WelcomePagerAdapter();
|
||||
private boolean isQuiz;
|
||||
private AlertDialog.Builder dialogBuilder;
|
||||
private AlertDialog dialog;
|
||||
|
||||
/**
|
||||
* Initialises exiting fields and dependencies
|
||||
*
|
||||
* @param savedInstanceState WelcomeActivity bundled data
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityWelcomeBinding.inflate(getLayoutInflater());
|
||||
final View view = binding.getRoot();
|
||||
setContentView(view);
|
||||
|
||||
if (getIntent() != null) {
|
||||
final Bundle bundle = getIntent().getExtras();
|
||||
if (bundle != null) {
|
||||
isQuiz = bundle.getBoolean("isQuiz");
|
||||
}
|
||||
} else {
|
||||
isQuiz = false;
|
||||
}
|
||||
|
||||
// Enable skip button if beta flavor
|
||||
if (ConfigUtils.isBetaFlavour()) {
|
||||
binding.finishTutorialButton.setVisibility(View.VISIBLE);
|
||||
|
||||
dialogBuilder = new AlertDialog.Builder(this);
|
||||
copyrightBinding = PopupForCopyrightBinding.inflate(getLayoutInflater());
|
||||
final View contactPopupView = copyrightBinding.getRoot();
|
||||
dialogBuilder.setView(contactPopupView);
|
||||
dialogBuilder.setCancelable(false);
|
||||
dialog = dialogBuilder.create();
|
||||
dialog.show();
|
||||
|
||||
copyrightBinding.buttonOk.setOnClickListener(v -> dialog.dismiss());
|
||||
}
|
||||
|
||||
binding.welcomePager.setAdapter(adapter);
|
||||
binding.welcomePagerIndicator.setViewPager(binding.welcomePager);
|
||||
|
||||
binding.finishTutorialButton.setOnClickListener(v -> finishTutorial());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* References WelcomePageAdapter to null before the activity is destroyed
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (isQuiz) {
|
||||
final Intent i = new Intent(this, QuizActivity.class);
|
||||
startActivity(i);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a way to change current activity to WelcomeActivity
|
||||
*
|
||||
* @param context Activity context
|
||||
*/
|
||||
public static void startYourself(final Context context) {
|
||||
final Intent welcomeIntent = new Intent(context, WelcomeActivity.class);
|
||||
context.startActivity(welcomeIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override onBackPressed() to go to previous tutorial 'pages' if not on first page
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (binding.welcomePager.getCurrentItem() != 0) {
|
||||
binding.welcomePager.setCurrentItem(binding.welcomePager.getCurrentItem() - 1, true);
|
||||
} else {
|
||||
if (defaultKvStore.getBoolean("firstrun", true)) {
|
||||
finishAffinity();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void finishTutorial() {
|
||||
defaultKvStore.putBoolean("firstrun", false);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
78
app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt
Normal file
78
app/src/main/java/fr/free/nrw/commons/WelcomeActivity.kt
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import fr.free.nrw.commons.databinding.ActivityWelcomeBinding
|
||||
import fr.free.nrw.commons.databinding.PopupForCopyrightBinding
|
||||
import fr.free.nrw.commons.quiz.QuizActivity
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
|
||||
class WelcomeActivity : BaseActivity() {
|
||||
private var binding: ActivityWelcomeBinding? = null
|
||||
private var isQuiz = false
|
||||
|
||||
/**
|
||||
* Initialises exiting fields and dependencies
|
||||
*
|
||||
* @param savedInstanceState WelcomeActivity bundled data
|
||||
*/
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding!!.root)
|
||||
|
||||
isQuiz = intent?.extras?.getBoolean("isQuiz", false) ?: false
|
||||
|
||||
// Enable skip button if beta flavor
|
||||
if (isBetaFlavour) {
|
||||
binding!!.finishTutorialButton.visibility = View.VISIBLE
|
||||
|
||||
val copyrightBinding = PopupForCopyrightBinding.inflate(layoutInflater)
|
||||
|
||||
val dialog = AlertDialog.Builder(this)
|
||||
.setView(copyrightBinding.root)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
dialog.show()
|
||||
|
||||
copyrightBinding.buttonOk.setOnClickListener { v: View? -> dialog.dismiss() }
|
||||
}
|
||||
|
||||
val adapter = WelcomePagerAdapter()
|
||||
binding!!.welcomePager.adapter = adapter
|
||||
binding!!.welcomePagerIndicator.setViewPager(binding!!.welcomePager)
|
||||
binding!!.finishTutorialButton.setOnClickListener { v: View? -> finishTutorial() }
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
if (isQuiz) {
|
||||
startActivity(Intent(this, QuizActivity::class.java))
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (binding!!.welcomePager.currentItem != 0) {
|
||||
binding!!.welcomePager.setCurrentItem(binding!!.welcomePager.currentItem - 1, true)
|
||||
} else {
|
||||
if (defaultKvStore.getBoolean("firstrun", true)) {
|
||||
finishAffinity()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun finishTutorial() {
|
||||
defaultKvStore.putBoolean("firstrun", false)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.startWelcome() {
|
||||
startActivity(Intent(this, WelcomeActivity::class.java))
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
|
||||
public class WelcomePagerAdapter extends PagerAdapter {
|
||||
private static final int[] PAGE_LAYOUTS = new int[]{
|
||||
R.layout.welcome_wikipedia,
|
||||
R.layout.welcome_do_upload,
|
||||
R.layout.welcome_dont_upload,
|
||||
R.layout.welcome_image_example,
|
||||
R.layout.welcome_final
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets total number of layouts
|
||||
* @return Number of layouts
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return PAGE_LAYOUTS.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares given view with provided object
|
||||
* @param view Adapter view
|
||||
* @param object Adapter object
|
||||
* @return Equality between view and object
|
||||
*/
|
||||
@Override
|
||||
public boolean isViewFromObject(View view, Object object) {
|
||||
return (view == object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object instantiateItem(ViewGroup container, int position) {
|
||||
LayoutInflater inflater = LayoutInflater.from(container.getContext());
|
||||
ViewGroup layout = (ViewGroup) inflater.inflate(PAGE_LAYOUTS[position], container, false);
|
||||
|
||||
// If final page
|
||||
if (position == PAGE_LAYOUTS.length - 1) {
|
||||
// Add link to more information
|
||||
TextView moreInfo = layout.findViewById(R.id.welcomeInfo);
|
||||
Utils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text, container.getContext());
|
||||
moreInfo.setOnClickListener(view -> Utils.handleWebUrl(
|
||||
container.getContext(),
|
||||
Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents")
|
||||
));
|
||||
|
||||
// Handle click of finishTutorialButton ("YES!" button) inside layout
|
||||
layout.findViewById(R.id.finishTutorialButton)
|
||||
.setOnClickListener(view -> ((WelcomeActivity) container.getContext()).finishTutorial());
|
||||
}
|
||||
|
||||
container.addView(layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a way to remove an item from container
|
||||
* @param container Adapter view group container
|
||||
* @param position Index of item
|
||||
* @param obj Adapter object
|
||||
*/
|
||||
@Override
|
||||
public void destroyItem(ViewGroup container, int position, Object obj) {
|
||||
container.removeView((View) obj);
|
||||
}
|
||||
}
|
||||
70
app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt
Normal file
70
app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.kt
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package fr.free.nrw.commons
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.viewpager.widget.PagerAdapter
|
||||
import fr.free.nrw.commons.utils.UnderlineUtils.setUnderlinedText
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
|
||||
class WelcomePagerAdapter : PagerAdapter() {
|
||||
/**
|
||||
* Gets total number of layouts
|
||||
* @return Number of layouts
|
||||
*/
|
||||
override fun getCount(): Int = PAGE_LAYOUTS.size
|
||||
|
||||
/**
|
||||
* Compares given view with provided object
|
||||
* @param view Adapter view
|
||||
* @param obj Adapter object
|
||||
* @return Equality between view and object
|
||||
*/
|
||||
override fun isViewFromObject(view: View, obj: Any): Boolean = (view === obj)
|
||||
|
||||
/**
|
||||
* Provides a way to remove an item from container
|
||||
* @param container Adapter view group container
|
||||
* @param position Index of item
|
||||
* @param obj Adapter object
|
||||
*/
|
||||
override fun destroyItem(container: ViewGroup, position: Int, obj: Any) =
|
||||
container.removeView(obj as View)
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val inflater = LayoutInflater.from(container.context)
|
||||
val layout = inflater.inflate(PAGE_LAYOUTS[position], container, false) as ViewGroup
|
||||
|
||||
// If final page
|
||||
if (position == PAGE_LAYOUTS.size - 1) {
|
||||
// Add link to more information
|
||||
val moreInfo = layout.findViewById<TextView>(R.id.welcomeInfo)
|
||||
setUnderlinedText(moreInfo, R.string.welcome_help_button_text)
|
||||
moreInfo.setOnClickListener {
|
||||
handleWebUrl(
|
||||
container.context,
|
||||
"https://commons.wikimedia.org/wiki/Help:Contents".toUri()
|
||||
)
|
||||
}
|
||||
|
||||
// Handle click of finishTutorialButton ("YES!" button) inside layout
|
||||
layout.findViewById<View>(R.id.finishTutorialButton)
|
||||
.setOnClickListener { view: View? -> (container.context as WelcomeActivity).finishTutorial() }
|
||||
}
|
||||
|
||||
container.addView(layout)
|
||||
return layout
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val PAGE_LAYOUTS = intArrayOf(
|
||||
R.layout.welcome_wikipedia,
|
||||
R.layout.welcome_do_upload,
|
||||
R.layout.welcome_dont_upload,
|
||||
R.layout.welcome_image_example,
|
||||
R.layout.welcome_final
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat
|
|||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.login.LoginCallback
|
||||
import fr.free.nrw.commons.auth.login.LoginClient
|
||||
import fr.free.nrw.commons.auth.login.LoginResult
|
||||
|
|
@ -38,6 +37,7 @@ import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags
|
|||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
|
|
@ -254,10 +254,10 @@ class LoginActivity : AccountAuthenticatorActivity() {
|
|||
}
|
||||
|
||||
private fun forgotPassword() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
|
||||
handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
|
||||
|
||||
private fun onPrivacyPolicyClicked() =
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
|
||||
|
||||
private fun signUp() =
|
||||
startActivity(Intent(this, SignupActivity::class.java))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.databinding.LayoutCampaginBinding
|
||||
|
|
@ -16,6 +15,7 @@ import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort
|
|||
import fr.free.nrw.commons.utils.DateUtil.getExtraShortDateString
|
||||
import fr.free.nrw.commons.utils.SwipableCardView
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import timber.log.Timber
|
||||
import java.text.ParseException
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ class CampaignView : SwipableCardView {
|
|||
if (it.isWLMCampaign) {
|
||||
((context) as MainActivity).showNearby()
|
||||
} else {
|
||||
Utils.handleWebUrl(context, Uri.parse(it.link))
|
||||
handleWebUrl(context, Uri.parse(it.link))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CampaignsPresenter @Inject constructor(
|
|||
private val okHttpJsonApiClient: OkHttpJsonApiClient?,
|
||||
@param:Named(IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler
|
||||
) : BasePresenter<ICampaignsView?> {
|
||||
) : BasePresenter<ICampaignsView> {
|
||||
private var view: ICampaignsView? = null
|
||||
private var disposable: Disposable? = null
|
||||
private var campaign: Campaign? = null
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
package fr.free.nrw.commons.campaigns
|
||||
|
||||
import fr.free.nrw.commons.MvpView
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
|
||||
/**
|
||||
* Interface which defines the view contracts of the campaign view
|
||||
*/
|
||||
interface ICampaignsView : MvpView {
|
||||
interface ICampaignsView {
|
||||
fun showCampaigns(campaign: Campaign?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.ViewPagerAdapter
|
||||
import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment
|
||||
|
|
@ -23,6 +23,9 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment
|
|||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import fr.free.nrw.commons.wikidata.model.page.PageTitle
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -56,7 +59,7 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
val view = binding.root
|
||||
setContentView(view)
|
||||
supportFragmentManager = getSupportFragmentManager()
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
binding.viewPager.offscreenPageLimit = 2
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
|
|
@ -80,8 +83,6 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
* Set the fragments according to the tab selected in the viewPager.
|
||||
*/
|
||||
private fun setTabs() {
|
||||
val fragmentList = mutableListOf<Fragment>()
|
||||
val titleList = mutableListOf<String>()
|
||||
categoriesMediaFragment = CategoriesMediaFragment()
|
||||
val subCategoryListFragment = SubCategoriesFragment()
|
||||
val parentCategoriesFragment = ParentCategoriesFragment()
|
||||
|
|
@ -96,13 +97,12 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
|
||||
viewModel.onCheckIfBookmarked(categoryName!!)
|
||||
}
|
||||
fragmentList.add(categoriesMediaFragment)
|
||||
titleList.add("MEDIA")
|
||||
fragmentList.add(subCategoryListFragment)
|
||||
titleList.add("SUBCATEGORIES")
|
||||
fragmentList.add(parentCategoriesFragment)
|
||||
titleList.add("PARENT CATEGORIES")
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList)
|
||||
|
||||
viewPagerAdapter.setTabs(
|
||||
R.string.title_for_media to categoriesMediaFragment,
|
||||
R.string.title_for_subcategories to subCategoryListFragment,
|
||||
R.string.title_for_parent_categories to parentCategoriesFragment
|
||||
)
|
||||
viewPagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
@ -199,8 +199,9 @@ class CategoryDetailsActivity : BaseActivity(),
|
|||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.menu_browser_current_category -> {
|
||||
val title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName)
|
||||
Utils.handleWebUrl(this, Uri.parse(title.canonicalUri))
|
||||
val title = PageTitle(CATEGORY_PREFIX + categoryName, WikiSite(COMMONS_URL))
|
||||
|
||||
handleWebUrl(this, Uri.parse(title.canonicalUri))
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import fr.free.nrw.commons.BasePresenter
|
|||
interface ContributionsContract {
|
||||
|
||||
interface View {
|
||||
fun showMessage(localizedMessage: String)
|
||||
fun getContext(): Context?
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import androidx.work.WorkManager
|
|||
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.campaigns.CampaignView
|
||||
import fr.free.nrw.commons.campaigns.CampaignsPresenter
|
||||
|
|
@ -64,6 +63,9 @@ import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween
|
|||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.isMonumentsEnabled
|
||||
import fr.free.nrw.commons.utils.wLMEndDate
|
||||
import fr.free.nrw.commons.utils.wLMStartDate
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
|
@ -242,8 +244,8 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
private fun initWLMCampaign() {
|
||||
wlmCampaign = Campaign(
|
||||
getString(R.string.wlm_campaign_title),
|
||||
getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(),
|
||||
Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true
|
||||
getString(R.string.wlm_campaign_description), wLMStartDate,
|
||||
wLMEndDate, NearbyParentFragment.WLM_URL, true
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -729,7 +731,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
* of campaigns on the campaigns card
|
||||
*/
|
||||
private fun fetchCampaigns() {
|
||||
if (Utils.isMonumentsEnabled(Date())) {
|
||||
if (isMonumentsEnabled) {
|
||||
if (binding != null) {
|
||||
binding!!.campaignsView.setCampaign(wlmCampaign)
|
||||
binding!!.campaignsView.visibility = View.VISIBLE
|
||||
|
|
@ -743,10 +745,6 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
|
|||
}
|
||||
}
|
||||
|
||||
override fun showMessage(message: String) {
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun showCampaigns(campaign: Campaign?) {
|
||||
if (campaign != null && !isUserProfile) {
|
||||
if (binding != null) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class ContributionsListContract {
|
|||
fun showNoContributionsUI(shouldShow: Boolean)
|
||||
}
|
||||
|
||||
interface UserActionListener : BasePresenter<View?> {
|
||||
interface UserActionListener : BasePresenter<View> {
|
||||
fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator
|
|||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.MediaDataExtractor
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance
|
||||
import fr.free.nrw.commons.databinding.FragmentContributionsListBinding
|
||||
|
|
@ -41,6 +40,8 @@ import fr.free.nrw.commons.profile.ProfileActivity
|
|||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import javax.inject.Inject
|
||||
|
|
@ -527,14 +528,13 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
|
|||
*/
|
||||
override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) {
|
||||
if (copyWikicode) {
|
||||
val wikicode = contribution!!.media.wikiCode
|
||||
Utils.copy("wikicode", wikicode, context)
|
||||
requireContext().copyToClipboard("wikicode", contribution!!.media.wikiCode)
|
||||
}
|
||||
|
||||
val url =
|
||||
languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace
|
||||
?.getWikipediaPageTitle())
|
||||
Utils.handleWebUrl(context, Uri.parse(url))
|
||||
handleWebUrl(requireContext(), Uri.parse(url))
|
||||
}
|
||||
|
||||
fun getContributionStateAt(position: Int): Int {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import androidx.fragment.app.FragmentManager
|
|||
import androidx.work.ExistingWorkPolicy
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.WelcomeActivity
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkFragment
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment.Companion.newInstance
|
||||
|
|
@ -33,6 +32,7 @@ import fr.free.nrw.commons.notification.NotificationActivity.Companion.startYour
|
|||
import fr.free.nrw.commons.notification.NotificationController
|
||||
import fr.free.nrw.commons.quiz.QuizChecker
|
||||
import fr.free.nrw.commons.settings.SettingsFragment
|
||||
import fr.free.nrw.commons.startWelcome
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.UploadProgressActivity
|
||||
import fr.free.nrw.commons.upload.worker.WorkRequestHelper.Companion.makeOneTimeWorkRequest
|
||||
|
|
@ -517,7 +517,7 @@ after opening the app.
|
|||
(!applicationKvStore!!.getBoolean("login_skipped"))
|
||||
) {
|
||||
defaultKvStore.putBoolean("inAppCameraFirstRun", true)
|
||||
WelcomeActivity.startYourself(this)
|
||||
startWelcome()
|
||||
}
|
||||
|
||||
retryAllFailedUploads()
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ class DeleteHelper @Inject constructor(
|
|||
media: Media?,
|
||||
reason: String?
|
||||
): Single<Boolean>? {
|
||||
|
||||
if(context == null && media == null) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -86,7 +85,6 @@ class DeleteHelper @Inject constructor(
|
|||
* @return
|
||||
*/
|
||||
private fun delete(media: Media, reason: String): Observable<Boolean> {
|
||||
Timber.d("thread is delete %s", Thread.currentThread().name)
|
||||
val summary = "Nominating ${media.filename} for deletion."
|
||||
val calendar = Calendar.getInstance()
|
||||
val fileDeleteString = """
|
||||
|
|
|
|||
|
|
@ -2,21 +2,19 @@ package fr.free.nrw.commons.delete
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
||||
import fr.free.nrw.commons.utils.DateUtil
|
||||
import java.util.Locale
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.profile.achievements.FeedbackResponse
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.utils.DateUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.rx2.rxSingle
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* This class handles the reason for deleting a Media object
|
||||
|
|
@ -29,6 +27,8 @@ class ReasonBuilder @Inject constructor(
|
|||
private val viewUtilWrapper: ViewUtilWrapper
|
||||
) {
|
||||
|
||||
private val defaultFileUsagePageSize = 10
|
||||
|
||||
/**
|
||||
* To process the reason and append the media's upload date and uploaded_by_me string
|
||||
* @param media
|
||||
|
|
@ -39,7 +39,7 @@ class ReasonBuilder @Inject constructor(
|
|||
if (media == null || reason == null) {
|
||||
return Single.just("Not known")
|
||||
}
|
||||
return fetchArticleNumber(media, reason)
|
||||
return getAndAppendFileUsage(media, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -54,27 +54,36 @@ class ReasonBuilder @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun fetchArticleNumber(media: Media, reason: String): Single<String> {
|
||||
return if (checkAccount()) {
|
||||
okHttpJsonApiClient
|
||||
.getAchievements(sessionManager.userName)
|
||||
.map { feedbackResponse -> appendArticlesUsed(feedbackResponse, media, reason) }
|
||||
} else {
|
||||
Single.just("")
|
||||
private fun getAndAppendFileUsage(media: Media, reason: String): Single<String> {
|
||||
return rxSingle(context = Dispatchers.IO) {
|
||||
if (!checkAccount()) return@rxSingle ""
|
||||
|
||||
try {
|
||||
val globalFileUsage = okHttpJsonApiClient.getGlobalFileUsages(
|
||||
fileName = media.filename,
|
||||
pageSize = defaultFileUsagePageSize
|
||||
)
|
||||
val globalUsages = globalFileUsage?.query?.pages?.sumOf { it.fileUsage.size } ?: 0
|
||||
|
||||
appendArticlesUsed(globalUsages, media, reason)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error fetching file usage")
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the uploaded_by_me string, the upload date, name of articles using images
|
||||
* Takes the uploaded_by_me string, the upload date, no. of articles using images
|
||||
* and appends it to the received reason
|
||||
* @param feedBack object
|
||||
* @param fileUsages No. of files/articles using this image
|
||||
* @param media whose upload data is to be fetched
|
||||
* @param reason
|
||||
* @param reason string to be appended
|
||||
*/
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private fun appendArticlesUsed(feedBack: FeedbackResponse, media: Media, reason: String): String {
|
||||
private fun appendArticlesUsed(fileUsages: Int, media: Media, reason: String): String {
|
||||
val reason1Template = context.getString(R.string.uploaded_by_myself)
|
||||
return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), feedBack.articlesUsingImages)
|
||||
return reason + String.format(Locale.getDefault(), reason1Template, prettyUploadedDate(media), fileUsages)
|
||||
.also { Timber.i("New Reason %s", it) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.explore;
|
||||
|
||||
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE;
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -23,10 +24,12 @@ import fr.free.nrw.commons.kvstore.JsonKvStore;
|
|||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.ActivityUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import kotlin.Pair;
|
||||
|
||||
public class ExploreFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
|
|
@ -70,7 +73,7 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
loadNearbyMapData();
|
||||
binding = FragmentExploreBinding.inflate(inflater, container, false);
|
||||
|
||||
viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager(),
|
||||
viewPagerAdapter = new ViewPagerAdapter(requireContext(), getChildFragmentManager(),
|
||||
FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
|
||||
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
|
|
@ -111,9 +114,6 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
* Sets the titles in the tabLayout and fragments in the viewPager
|
||||
*/
|
||||
public void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
|
||||
Bundle featuredArguments = new Bundle();
|
||||
featuredArguments.putString("categoryName", FEATURED_IMAGES_CATEGORY);
|
||||
|
||||
|
|
@ -133,19 +133,15 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
|
|||
featuredRootFragment = new ExploreListRootFragment(featuredArguments);
|
||||
mobileRootFragment = new ExploreListRootFragment(mobileArguments);
|
||||
mapRootFragment = new ExploreMapRootFragment(mapArguments);
|
||||
fragmentList.add(featuredRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_featured).toUpperCase(Locale.ROOT));
|
||||
|
||||
fragmentList.add(mobileRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_mobile).toUpperCase(Locale.ROOT));
|
||||
|
||||
fragmentList.add(mapRootFragment);
|
||||
titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT));
|
||||
|
||||
((MainActivity) getActivity()).showTabs();
|
||||
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.explore_tab_title_featured, featuredRootFragment),
|
||||
pairOf(R.string.explore_tab_title_mobile, mobileRootFragment),
|
||||
pairOf(R.string.explore_tab_title_map, mapRootFragment)
|
||||
);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.explore;
|
||||
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
|
|
@ -26,11 +28,13 @@ import fr.free.nrw.commons.utils.FragmentUtils;
|
|||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import kotlin.Pair;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -65,7 +69,7 @@ public class SearchActivity extends BaseActivity
|
|||
binding.toolbarSearch.setNavigationOnClickListener(v->onBackPressed());
|
||||
supportFragmentManager = getSupportFragmentManager();
|
||||
setSearchHistoryFragment();
|
||||
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
viewPagerAdapter = new ViewPagerAdapter(this, getSupportFragmentManager());
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
binding.viewPager.setOffscreenPageLimit(2); // Because we want all the fragments to be alive
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
||||
|
|
@ -90,19 +94,15 @@ public class SearchActivity extends BaseActivity
|
|||
* Sets the titles in the tabLayout and fragments in the viewPager
|
||||
*/
|
||||
public void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
searchMediaFragment = new SearchMediaFragment();
|
||||
searchDepictionsFragment = new SearchDepictionsFragment();
|
||||
searchCategoryFragment= new SearchCategoryFragment();
|
||||
fragmentList.add(searchMediaFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_media).toUpperCase(Locale.ROOT));
|
||||
fragmentList.add(searchCategoryFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_categories).toUpperCase(Locale.ROOT));
|
||||
fragmentList.add(searchDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.search_tab_title_depictions).toUpperCase(Locale.ROOT));
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.search_tab_title_media, searchMediaFragment),
|
||||
pairOf(R.string.search_tab_title_categories, searchCategoryFragment),
|
||||
pairOf(R.string.search_tab_title_depictions, searchDepictionsFragment)
|
||||
);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
getCompositeDisposable().add(RxSearchView.queryTextChanges(binding.searchBox)
|
||||
.takeUntil(RxView.detaches(binding.searchBox))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package fr.free.nrw.commons.explore.depictions;
|
||||
|
||||
import static fr.free.nrw.commons.ViewPagerAdapter.pairOf;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
|
@ -8,16 +11,11 @@ import android.view.Menu;
|
|||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao;
|
||||
import fr.free.nrw.commons.category.CategoryImagesCallback;
|
||||
|
|
@ -34,8 +32,10 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import kotlin.Pair;
|
||||
|
||||
/**
|
||||
* Activity to show depiction media, parent classes and child classes of depicted items in Explore
|
||||
|
|
@ -69,7 +69,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
setContentView(binding.getRoot());
|
||||
compositeDisposable = new CompositeDisposable();
|
||||
supportFragmentManager = getSupportFragmentManager();
|
||||
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
viewPagerAdapter = new ViewPagerAdapter(this, getSupportFragmentManager());
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
binding.viewPager.setOffscreenPageLimit(2);
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
||||
|
|
@ -108,8 +108,6 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
* Set the fragments according to the tab selected in the viewPager.
|
||||
*/
|
||||
private void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
depictionImagesListFragment = new DepictedImagesFragment();
|
||||
ChildDepictionsFragment childDepictionsFragment = new ChildDepictionsFragment();
|
||||
ParentDepictionsFragment parentDepictionsFragment = new ParentDepictionsFragment();
|
||||
|
|
@ -123,13 +121,12 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
parentDepictionsFragment.setArguments(arguments);
|
||||
childDepictionsFragment.setArguments(arguments);
|
||||
}
|
||||
fragmentList.add(depictionImagesListFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_media));
|
||||
fragmentList.add(childDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_child_classes));
|
||||
fragmentList.add(parentDepictionsFragment);
|
||||
titleList.add(getResources().getString(R.string.title_for_parent_classes));
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
|
||||
viewPagerAdapter.setTabs(
|
||||
pairOf(R.string.title_for_media, depictionImagesListFragment),
|
||||
pairOf(R.string.title_for_subcategories, childDepictionsFragment),
|
||||
pairOf(R.string.title_for_parent_categories, parentDepictionsFragment)
|
||||
);
|
||||
binding.viewPager.setOffscreenPageLimit(2);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
|
||||
|
|
@ -249,7 +246,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe
|
|||
case R.id.browser_actions_menu_items:
|
||||
String entityId=getIntent().getStringExtra("entityId");
|
||||
Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId);
|
||||
Utils.handleWebUrl(this, uri);
|
||||
handleWebUrl(this, uri);
|
||||
return true;
|
||||
case R.id.menu_bookmark_current_item:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package fr.free.nrw.commons.explore.map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.media.MediaClient;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
|
@ -23,6 +25,7 @@ public class ExploreMapCalls {
|
|||
* @param currentLatLng coordinates of search location
|
||||
* @return list of places obtained
|
||||
*/
|
||||
@NonNull
|
||||
List<Media> callCommonsQuery(final LatLng currentLatLng) {
|
||||
String coordinates = currentLatLng.getLatitude() + "|" + currentLatLng.getLongitude();
|
||||
return mediaClient.getMediaListFromGeoSearch(coordinates).blockingGet();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ package fr.free.nrw.commons.explore.map;
|
|||
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
|
||||
import static fr.free.nrw.commons.utils.GeoCoordinatesKt.handleGeoCoordinates;
|
||||
import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.SuppressLint;
|
||||
|
|
@ -36,7 +38,6 @@ import fr.free.nrw.commons.BaseMarker;
|
|||
import fr.free.nrw.commons.MapController;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.databinding.FragmentExploreMapBinding;
|
||||
|
|
@ -639,13 +640,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
|
|||
*/
|
||||
private void passInfoToSheet(final Place place) {
|
||||
binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener(
|
||||
view -> Utils.handleGeoCoordinates(getActivity(),
|
||||
view -> handleGeoCoordinates(requireActivity(),
|
||||
place.getLocation(), binding.mapView.getZoomLevelDouble()));
|
||||
|
||||
binding.bottomSheetDetailsBinding.commonsButton.setVisibility(
|
||||
place.hasCommonsLink() ? View.VISIBLE : View.GONE);
|
||||
binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener(
|
||||
view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
|
||||
view -> handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
|
||||
|
||||
int index = 0;
|
||||
for (Media media : mediaList) {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import fr.free.nrw.commons.explore.map.ExploreMapController.NearbyBaseMarkerThum
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
|
||||
import fr.free.nrw.commons.nearby.Place;
|
||||
import io.reactivex.Observable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
|
|
@ -182,7 +183,7 @@ public class ExploreMapPresenter
|
|||
exploreMapController
|
||||
.loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.currentLatLng,
|
||||
// Curlatlang will be used to calculate distances
|
||||
explorePlacesInfo.explorePlaceList,
|
||||
(List<Place>) explorePlacesInfo.explorePlaceList,
|
||||
exploreMapFragmentView.getContext(),
|
||||
this,
|
||||
explorePlacesInfo);
|
||||
|
|
@ -230,11 +231,7 @@ public class ExploreMapPresenter
|
|||
mylocation.setLongitude(exploreMapFragmentView.getLastMapFocus().getLongitude());
|
||||
Float distance = mylocation.distanceTo(dest_location);
|
||||
|
||||
if (distance > 2000.0 * 3 / 4) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return !(distance > 2000.0 * 3 / 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import fr.free.nrw.commons.CameraPosition
|
|||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import fr.free.nrw.commons.coordinates.CoordinateEditHelper
|
||||
|
|
@ -45,6 +44,7 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Compani
|
|||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM
|
||||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
|
|
@ -432,8 +432,8 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
|
|||
|
||||
position?.let {
|
||||
mapView?.zoomLevelDouble?.let { zoomLevel ->
|
||||
Utils.handleGeoCoordinates(this, it, zoomLevel)
|
||||
} ?: Utils.handleGeoCoordinates(this, it)
|
||||
handleGeoCoordinates(this, it, zoomLevel)
|
||||
} ?: handleGeoCoordinates(this, it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,11 +74,10 @@ import fr.free.nrw.commons.BuildConfig
|
|||
import fr.free.nrw.commons.CameraPosition
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.CommonsApplication.Companion.instance
|
||||
import fr.free.nrw.commons.locationpicker.LocationPicker
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.MediaDataExtractor
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.utils.UnderlineUtils
|
||||
import fr.free.nrw.commons.actions.ThanksClient
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
|
|
@ -102,6 +101,7 @@ import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity
|
|||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.language.AppLanguageLookUpTable
|
||||
import fr.free.nrw.commons.location.LocationServiceManager
|
||||
import fr.free.nrw.commons.locationpicker.LocationPicker
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
|
||||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.review.ReviewHelper
|
||||
|
|
@ -116,8 +116,13 @@ import fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources
|
|||
import fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction
|
||||
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.setUnderlinedText
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
|
@ -125,6 +130,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
|||
import io.reactivex.schedulers.Schedulers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import timber.log.Timber
|
||||
import java.lang.String.format
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.Objects
|
||||
|
|
@ -314,8 +320,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
_binding = FragmentMediaDetailBinding.inflate(inflater, container, false)
|
||||
val view: View = binding.root
|
||||
|
||||
|
||||
Utils.setUnderlinedText(binding.seeMore, R.string.nominated_see_more, requireContext())
|
||||
binding.seeMore.setUnderlinedText(R.string.nominated_see_more)
|
||||
|
||||
if (isCategoryImage) {
|
||||
binding.authorLinearLayout.visibility = View.VISIBLE
|
||||
|
|
@ -907,7 +912,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
private fun onMediaDetailLicenceClicked() {
|
||||
val url: String? = media!!.licenseUrl
|
||||
if (!StringUtils.isBlank(url) && activity != null) {
|
||||
Utils.handleWebUrl(activity, Uri.parse(url))
|
||||
handleWebUrl(requireContext(), Uri.parse(url))
|
||||
} else {
|
||||
viewUtil.showShortToast(requireActivity(), getString(R.string.null_url))
|
||||
}
|
||||
|
|
@ -915,17 +920,17 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
|
||||
private fun onMediaDetailCoordinatesClicked() {
|
||||
if (media!!.coordinates != null && activity != null) {
|
||||
Utils.handleGeoCoordinates(activity, media!!.coordinates)
|
||||
handleGeoCoordinates(requireContext(), media!!.coordinates!!)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyWikicodeClicked() {
|
||||
val data: String =
|
||||
"[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]"
|
||||
Utils.copy("wikiCode", data, context)
|
||||
requireContext().copyToClipboard("wikiCode", data)
|
||||
Timber.d("Generated wikidata copy code: %s", data)
|
||||
|
||||
Toast.makeText(context, getString(R.string.wikicode_copied), Toast.LENGTH_SHORT)
|
||||
Toast.makeText(requireContext(), getString(R.string.wikicode_copied), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
|
||||
|
|
@ -1646,7 +1651,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
getString(R.string.cancel),
|
||||
{
|
||||
val reason: String = input.text.toString()
|
||||
onDeleteClickeddialogtext(reason)
|
||||
onDeleteClickedDialogText(reason)
|
||||
},
|
||||
{},
|
||||
input
|
||||
|
|
@ -1700,26 +1705,48 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
resultSingle
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
if (applicationKvStore.getBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl
|
||||
), false
|
||||
)
|
||||
) {
|
||||
applicationKvStore.remove(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
media!!.imageUrl
|
||||
)
|
||||
)
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
.subscribe(this::handleDeletionResult, this::handleDeletionError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables Progress Bar and Update delete button text.
|
||||
*/
|
||||
private fun disableProgressBar() {
|
||||
activity?.run {
|
||||
runOnUiThread(Runnable {
|
||||
binding.progressBarDeletion.visibility = View.GONE
|
||||
})
|
||||
} ?: return // Prevent NullPointerException when fragment is not attached to activity
|
||||
}
|
||||
|
||||
private fun handleDeletionResult(success: Boolean) {
|
||||
if (success) {
|
||||
binding.nominateDeletion.text = getString(R.string.nominated_for_deletion_btn)
|
||||
ViewUtil.showLongSnackbar(requireView(), getString(R.string.nominated_for_deletion))
|
||||
disableProgressBar()
|
||||
checkAndClearDeletionFlag()
|
||||
} else {
|
||||
disableProgressBar()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeletionError(throwable: Throwable) {
|
||||
throwable.printStackTrace()
|
||||
disableProgressBar()
|
||||
checkAndClearDeletionFlag()
|
||||
}
|
||||
|
||||
private fun checkAndClearDeletionFlag() {
|
||||
if (applicationKvStore
|
||||
.getBoolean(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl), false)
|
||||
) {
|
||||
applicationKvStore.remove(format(NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl))
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun onDeleteClickeddialogtext(reason: String) {
|
||||
private fun onDeleteClickedDialogText(reason: String) {
|
||||
applicationKvStore.putBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
|
|
@ -1736,27 +1763,12 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
resultSingletext
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
if (applicationKvStore.getBoolean(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA, media!!.imageUrl
|
||||
), false
|
||||
)
|
||||
) {
|
||||
applicationKvStore.remove(
|
||||
String.format(
|
||||
NOMINATING_FOR_DELETION_MEDIA,
|
||||
media!!.imageUrl
|
||||
)
|
||||
)
|
||||
callback!!.nominatingForDeletion(index)
|
||||
}
|
||||
}
|
||||
.subscribe(this::handleDeletionResult, this::handleDeletionError);
|
||||
}
|
||||
|
||||
private fun onSeeMoreClicked() {
|
||||
if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) {
|
||||
Utils.handleWebUrl(activity, Uri.parse(media!!.pageTitle.mobileUri))
|
||||
handleWebUrl(requireContext(), Uri.parse(media!!.pageTitle.mobileUri))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package fr.free.nrw.commons.media;
|
||||
|
||||
import static fr.free.nrw.commons.Utils.handleWebUrl;
|
||||
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
|
@ -31,7 +31,7 @@ import com.google.android.material.snackbar.Snackbar;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.utils.ClipboardUtils;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.bookmarks.models.Bookmark;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||
|
|
@ -216,7 +216,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
return true;
|
||||
case R.id.menu_copy_link:
|
||||
String uri = m.getPageTitle().getCanonicalUri();
|
||||
Utils.copy("shareLink", uri, requireContext());
|
||||
ClipboardUtils.copy("shareLink", uri, requireContext());
|
||||
Timber.d("Copied share link to clipboard: %s", uri);
|
||||
Toast.makeText(requireContext(), getString(R.string.menu_link_copied),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package fr.free.nrw.commons.media
|
||||
|
||||
import fr.free.nrw.commons.OkHttpConnectionFactory.UnsuccessfulResponseInterceptor.SUPPRESS_ERROR_LOG_HEADER
|
||||
import fr.free.nrw.commons.SUPPRESS_ERROR_LOG_HEADER
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.GET
|
||||
|
|
|
|||
|
|
@ -281,6 +281,7 @@ class OkHttpJsonApiClient @Inject constructor(
|
|||
FeedbackResponse::class.java
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
return@fromCallable FeedbackResponse(0, 0, 0, FeaturedImages(0, 0), 0, "")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import fr.free.nrw.commons.BuildConfig
|
|||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.CommonsApplication.ActivityLogoutListener
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.WelcomeActivity
|
||||
import fr.free.nrw.commons.actions.PageEditClient
|
||||
import fr.free.nrw.commons.databinding.FragmentMoreBottomSheetBinding
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
|
|
@ -32,6 +31,7 @@ import fr.free.nrw.commons.logging.CommonsLogSender
|
|||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.review.ReviewActivity
|
||||
import fr.free.nrw.commons.settings.SettingsActivity
|
||||
import fr.free.nrw.commons.startWelcome
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
|
@ -241,7 +241,7 @@ class MoreBottomSheetFragment : BottomSheetDialogFragment() {
|
|||
}
|
||||
|
||||
fun onTutorialClicked() {
|
||||
WelcomeActivity.startYourself(requireActivity())
|
||||
requireContext().startWelcome()
|
||||
}
|
||||
|
||||
fun onSettingsClicked() {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import android.location.Location;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams.Radial;
|
||||
import fr.free.nrw.commons.nearby.model.NearbyQueryParams.Rectangular;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
|
@ -46,13 +50,14 @@ public class NearbyPlaces {
|
|||
* @param customQuery
|
||||
* @return list of places obtained
|
||||
*/
|
||||
@NonNull
|
||||
List<Place> radiusExpander(final LatLng currentLatLng, final String lang,
|
||||
final boolean returnClosestResult, @Nullable final String customQuery) throws Exception {
|
||||
|
||||
final int minResults;
|
||||
final double maxRadius;
|
||||
|
||||
List<Place> places = Collections.emptyList();
|
||||
List<Place> places = emptyList();
|
||||
|
||||
// If returnClosestResult is true, then this means that we are trying to get closest point
|
||||
// to use in cardView in Contributions fragment
|
||||
|
|
@ -113,6 +118,7 @@ public class NearbyPlaces {
|
|||
* @return A list of places obtained from the Wikidata query.
|
||||
* @throws Exception If an error occurs during the retrieval process.
|
||||
*/
|
||||
@NonNull
|
||||
public List<Place> getFromWikidataQuery(
|
||||
final fr.free.nrw.commons.location.LatLng centerPoint,
|
||||
final fr.free.nrw.commons.location.LatLng screenTopRight,
|
||||
|
|
@ -120,11 +126,11 @@ public class NearbyPlaces {
|
|||
final boolean shouldQueryForMonuments,
|
||||
@Nullable final String customQuery) throws Exception {
|
||||
if (customQuery != null) {
|
||||
return okHttpJsonApiClient
|
||||
.getNearbyPlaces(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
shouldQueryForMonuments,
|
||||
customQuery);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
|
||||
final int lowerLimit = 1000, upperLimit = 1500;
|
||||
|
|
@ -141,9 +147,10 @@ public class NearbyPlaces {
|
|||
final int itemCount = okHttpJsonApiClient.getNearbyItemCount(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft));
|
||||
if (itemCount < upperLimit) {
|
||||
return okHttpJsonApiClient.getNearbyPlaces(
|
||||
new NearbyQueryParams.Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Rectangular(screenTopRight, screenBottomLeft), lang,
|
||||
shouldQueryForMonuments, null);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +182,10 @@ public class NearbyPlaces {
|
|||
maxRadius = targetRadius - 1;
|
||||
}
|
||||
}
|
||||
return okHttpJsonApiClient.getNearbyPlaces(
|
||||
new NearbyQueryParams.Radial(centerPoint, targetRadius / 100f), lang, shouldQueryForMonuments,
|
||||
final List<Place> nearbyPlaces = okHttpJsonApiClient.getNearbyPlaces(
|
||||
new Radial(centerPoint, targetRadius / 100f), lang, shouldQueryForMonuments,
|
||||
null);
|
||||
return nearbyPlaces != null ? nearbyPlaces : emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ import androidx.activity.result.ActivityResultLauncher
|
|||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.ActivityUtils
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.wikidata.WikidataConstants
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
|
@ -104,7 +105,7 @@ class CommonPlaceClickActions
|
|||
|
||||
fun onDirectionsClicked(): (Place) -> Unit =
|
||||
{
|
||||
Utils.handleGeoCoordinates(activity, it.getLocation())
|
||||
handleGeoCoordinates(activity, it.getLocation())
|
||||
}
|
||||
|
||||
private fun storeSharedPrefs(selectedPlace: Place) {
|
||||
|
|
@ -113,7 +114,7 @@ class CommonPlaceClickActions
|
|||
}
|
||||
|
||||
private fun openWebView(link: Uri): Boolean {
|
||||
Utils.handleWebUrl(activity, link)
|
||||
handleWebUrl(activity, link)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,12 +58,10 @@ import fr.free.nrw.commons.CommonsApplication
|
|||
import fr.free.nrw.commons.MapController.NearbyPlacesInfo
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageLoader
|
||||
import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
import fr.free.nrw.commons.filepicker.FilePicker
|
||||
|
|
@ -76,7 +74,6 @@ import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType
|
|||
import fr.free.nrw.commons.location.LocationUpdateListener
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider
|
||||
import fr.free.nrw.commons.navtab.NavTab
|
||||
import fr.free.nrw.commons.nearby.BottomSheetAdapter
|
||||
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener
|
||||
|
|
@ -105,6 +102,10 @@ import fr.free.nrw.commons.utils.NearbyFABUtils.removeAnchorFromFAB
|
|||
import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.copyToClipboard
|
||||
import fr.free.nrw.commons.utils.handleGeoCoordinates
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.isMonumentsEnabled
|
||||
import fr.free.nrw.commons.wikidata.WikidataConstants
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener
|
||||
|
|
@ -123,6 +124,7 @@ import org.osmdroid.views.CustomZoomButtonsController
|
|||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.MapEventsOverlay
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
import org.osmdroid.views.overlay.Overlay
|
||||
import org.osmdroid.views.overlay.ScaleBarOverlay
|
||||
import org.osmdroid.views.overlay.ScaleDiskOverlay
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
|
|
@ -139,7 +141,6 @@ import java.util.UUID
|
|||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.sql.DataSource
|
||||
import kotlin.concurrent.Volatile
|
||||
|
||||
|
||||
|
|
@ -267,6 +268,9 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
private var dataList: MutableList<BottomSheetItem>? = null
|
||||
private var bottomSheetAdapter: BottomSheetAdapter? = null
|
||||
|
||||
private var userLocationOverlay: Overlay? = null
|
||||
private var userLocationErrorOverlay: Overlay? = null
|
||||
|
||||
private val galleryPickLauncherForResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
controller?.handleActivityResultWithCallback(
|
||||
|
|
@ -463,7 +467,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
}
|
||||
}
|
||||
_isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true
|
||||
if (Utils.isMonumentsEnabled(Date())) {
|
||||
if (isMonumentsEnabled) {
|
||||
binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding?.rlContainerWlmMonthMessage?.visibility = View.GONE
|
||||
|
|
@ -724,7 +728,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
val targetP = GeoPoint(target.latitude, target.longitude)
|
||||
mapCenter = targetP
|
||||
binding?.map?.controller?.setCenter(targetP)
|
||||
recenterMarkerToPosition(targetP)
|
||||
updateUserLocationOverlays(targetP, true)
|
||||
if (!isCameFromExploreMap()) {
|
||||
moveCameraToPosition(targetP)
|
||||
}
|
||||
|
|
@ -832,7 +836,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
loadAnimations()
|
||||
setBottomSheetCallbacks()
|
||||
addActionToTitle()
|
||||
if (!Utils.isMonumentsEnabled(Date())) {
|
||||
if (!isMonumentsEnabled) {
|
||||
NearbyFilterState.setWlmSelected(false)
|
||||
}
|
||||
}
|
||||
|
|
@ -1013,11 +1017,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
*/
|
||||
private fun addActionToTitle() {
|
||||
binding!!.bottomSheetDetails.title.setOnLongClickListener { view ->
|
||||
Utils.copy(
|
||||
"place", binding!!.bottomSheetDetails.title.text.toString(),
|
||||
context
|
||||
requireContext().copyToClipboard(
|
||||
"place", binding!!.bottomSheetDetails.title.text.toString()
|
||||
)
|
||||
Toast.makeText(context, fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT)
|
||||
Toast.makeText(requireContext(), fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
true
|
||||
}
|
||||
|
|
@ -1576,7 +1579,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
searchLatLng,
|
||||
false,
|
||||
true,
|
||||
Utils.isMonumentsEnabled(Date()),
|
||||
isMonumentsEnabled,
|
||||
customQuery
|
||||
)
|
||||
}
|
||||
|
|
@ -1629,7 +1632,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
searchLatLng,
|
||||
false,
|
||||
true,
|
||||
Utils.isMonumentsEnabled(Date()),
|
||||
isMonumentsEnabled,
|
||||
customQuery
|
||||
)
|
||||
}
|
||||
|
|
@ -1863,6 +1866,8 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
lastKnownLocation = latLng
|
||||
NearbyController.currentLocation = lastKnownLocation
|
||||
presenter!!.updateMapAndList(locationChangeType)
|
||||
|
||||
updateUserLocationOverlays(GeoPoint(latLng.latitude, latLng.longitude), true)
|
||||
}
|
||||
|
||||
override fun onLocationChangedSignificantly(latLng: LatLng) {
|
||||
|
|
@ -2644,43 +2649,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
*/
|
||||
override fun clearAllMarkers() {
|
||||
binding!!.map.overlayManager.clear()
|
||||
binding!!.map.invalidate()
|
||||
val geoPoint = mapCenter
|
||||
if (geoPoint != null) {
|
||||
val diskOverlay =
|
||||
ScaleDiskOverlay(
|
||||
this.context,
|
||||
geoPoint, 2000, UnitOfMeasure.foot
|
||||
)
|
||||
val circlePaint = Paint()
|
||||
circlePaint.color = Color.rgb(128, 128, 128)
|
||||
circlePaint.style = Paint.Style.STROKE
|
||||
circlePaint.strokeWidth = 2f
|
||||
diskOverlay.setCirclePaint2(circlePaint)
|
||||
val diskPaint = Paint()
|
||||
diskPaint.color = Color.argb(40, 128, 128, 128)
|
||||
diskPaint.style = Paint.Style.FILL_AND_STROKE
|
||||
diskOverlay.setCirclePaint1(diskPaint)
|
||||
diskOverlay.setDisplaySizeMin(900)
|
||||
diskOverlay.setDisplaySizeMax(1700)
|
||||
binding!!.map.overlays.add(diskOverlay)
|
||||
val startMarker = Marker(
|
||||
binding!!.map
|
||||
)
|
||||
startMarker.position = geoPoint
|
||||
startMarker.setAnchor(
|
||||
Marker.ANCHOR_CENTER,
|
||||
Marker.ANCHOR_BOTTOM
|
||||
)
|
||||
startMarker.icon =
|
||||
getDrawable(
|
||||
this.requireContext(),
|
||||
fr.free.nrw.commons.R.drawable.current_location_marker
|
||||
)
|
||||
startMarker.title = "Your Location"
|
||||
startMarker.textLabelFontSize = 24
|
||||
binding!!.map.overlays.add(startMarker)
|
||||
|
||||
var geoPoint = mapCenter
|
||||
val lastLatLng = locationManager.getLastLocation()
|
||||
if (lastLatLng != null) {
|
||||
geoPoint = GeoPoint(lastLatLng.latitude, lastLatLng.longitude)
|
||||
}
|
||||
updateUserLocationOverlays(geoPoint, false)
|
||||
|
||||
val scaleBarOverlay = ScaleBarOverlay(binding!!.map)
|
||||
scaleBarOverlay.setScaleBarOffset(15, 25)
|
||||
val barPaint = Paint()
|
||||
|
|
@ -2690,6 +2666,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
binding!!.map.overlays.add(scaleBarOverlay)
|
||||
binding!!.map.overlays.add(mapEventsOverlay)
|
||||
binding!!.map.setMultiTouchControls(true)
|
||||
binding!!.map.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2700,45 +2677,149 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
private fun recenterMarkerToPosition(geoPoint: GeoPoint?) {
|
||||
geoPoint?.let {
|
||||
binding?.map?.controller?.setCenter(it)
|
||||
val overlays = binding?.map?.overlays ?: return@let
|
||||
|
||||
// Remove markers and disks using index-based removal
|
||||
var i = 0
|
||||
while (i < overlays.size) {
|
||||
when (overlays[i]) {
|
||||
is Marker, is ScaleDiskOverlay -> overlays.removeAt(i)
|
||||
else -> i++
|
||||
}
|
||||
}
|
||||
|
||||
// Add disk overlay
|
||||
ScaleDiskOverlay(context, it, 2000, UnitOfMeasure.foot).apply {
|
||||
setCirclePaint2(Paint().apply {
|
||||
color = Color.rgb(128, 128, 128)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2f
|
||||
})
|
||||
setCirclePaint1(Paint().apply {
|
||||
color = Color.argb(40, 128, 128, 128)
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
})
|
||||
setDisplaySizeMin(900)
|
||||
setDisplaySizeMax(1700)
|
||||
overlays.add(this)
|
||||
}
|
||||
|
||||
// Add marker
|
||||
Marker(binding?.map).apply {
|
||||
position = it
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
icon = getDrawable(context, R.drawable.current_location_marker)
|
||||
title = "Your Location"
|
||||
textLabelFontSize = 24
|
||||
overlays.add(this)
|
||||
}
|
||||
updateUserLocationOverlays(it, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user current location overlays (both the location and error overlays) by
|
||||
* replacing any existing location overlays with new overlays at the given GeoPoint. If there
|
||||
* are no existing location and error overlays, then new overlays are added.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's current location.
|
||||
* @param invalidate If true, the map overlays will be invalidated after the user
|
||||
* location overlays are updated/added. If false, the overlays will not be invalidated.
|
||||
*/
|
||||
private fun updateUserLocationOverlays(geoPoint: GeoPoint?, invalidate: Boolean) {
|
||||
geoPoint?.let{
|
||||
updateUserLocationOverlay(geoPoint)
|
||||
updateUserLocationErrorOverlay(geoPoint)
|
||||
}
|
||||
|
||||
if (invalidate) {
|
||||
binding!!.map.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user location error overlay by either replacing it with or adding a new one.
|
||||
*
|
||||
* If the user location error overlay is null, the new overlay is added. If the
|
||||
* overlay is not null, it is replaced by the new overlay.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's location
|
||||
*/
|
||||
private fun updateUserLocationErrorOverlay(geoPoint: GeoPoint) {
|
||||
val overlays = binding?.map?.overlays ?: return
|
||||
|
||||
// Multiply accuracy by 2 to get 95% confidence interval
|
||||
val accuracy = getCurrentLocationAccuracy() * 2
|
||||
val overlay = createCurrentLocationErrorOverlay(this.context, geoPoint,
|
||||
(accuracy).toInt(), UnitOfMeasure.meter)
|
||||
|
||||
val index = overlays.indexOf(userLocationErrorOverlay)
|
||||
|
||||
if (userLocationErrorOverlay == null || index == -1) {
|
||||
overlays.add(overlay)
|
||||
} else {
|
||||
overlays[index] = overlay
|
||||
}
|
||||
|
||||
userLocationErrorOverlay = overlay
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the user location overlay by either replacing it with or adding a new one.
|
||||
*
|
||||
* If the user location overlay is null, the new overlay is added. If the
|
||||
* overlay is not null, it is replaced by the new overlay.
|
||||
*
|
||||
* @param geoPoint The GeoPoint representing the user's location
|
||||
*/
|
||||
private fun updateUserLocationOverlay(geoPoint: GeoPoint) {
|
||||
val overlays = binding?.map?.overlays ?: return
|
||||
|
||||
val overlay = createCurrentLocationOverlay(geoPoint)
|
||||
|
||||
val index = overlays.indexOf(userLocationOverlay)
|
||||
|
||||
if (userLocationOverlay == null || index == -1) {
|
||||
overlays.add(overlay)
|
||||
} else {
|
||||
overlays[index] = overlay
|
||||
}
|
||||
|
||||
userLocationOverlay = overlay
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The accuracy of the current location with a confidence at the 68th percentile.
|
||||
* Units are in meters. Returning 0 may indicate failure.
|
||||
*/
|
||||
private fun getCurrentLocationAccuracy(): Float {
|
||||
var accuracy = 0f
|
||||
val lastLocation = locationManager.getLastLocation()
|
||||
if (lastLocation != null) {
|
||||
accuracy = lastLocation.accuracy
|
||||
}
|
||||
|
||||
return accuracy
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the current location overlay
|
||||
*
|
||||
* @param geoPoint The GeoPoint where the current location overlay will be placed.
|
||||
*
|
||||
* @return The current location overlay as a Marker
|
||||
*/
|
||||
private fun createCurrentLocationOverlay(geoPoint: GeoPoint): Marker {
|
||||
val currentLocationOverlay = Marker(
|
||||
binding!!.map
|
||||
)
|
||||
currentLocationOverlay.position = geoPoint
|
||||
currentLocationOverlay.icon =
|
||||
getDrawable(
|
||||
this.requireContext(),
|
||||
fr.free.nrw.commons.R.drawable.current_location_marker
|
||||
)
|
||||
currentLocationOverlay.title = "Your Location"
|
||||
currentLocationOverlay.textLabelFontSize = 24
|
||||
currentLocationOverlay.setAnchor(0.5f, 0.5f)
|
||||
|
||||
return currentLocationOverlay
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the location error overlay to show the user how accurate the current location
|
||||
* overlay is. The edge of the disk is the 95% confidence interval.
|
||||
*
|
||||
* @param context The Android context
|
||||
* @param point The user's location as a GeoPoint
|
||||
* @param value The radius of the disk
|
||||
* @param unitOfMeasure The unit of measurement of the value/disk radius.
|
||||
*
|
||||
* @return The location error overlay as a ScaleDiskOverlay.
|
||||
*/
|
||||
private fun createCurrentLocationErrorOverlay(context: Context?, point: GeoPoint, value: Int,
|
||||
unitOfMeasure: UnitOfMeasure): ScaleDiskOverlay {
|
||||
val scaleDisk = ScaleDiskOverlay(context, point, value, unitOfMeasure)
|
||||
|
||||
scaleDisk.setCirclePaint2(Paint().apply {
|
||||
color = Color.rgb(128, 128, 128)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2f
|
||||
})
|
||||
|
||||
scaleDisk.setCirclePaint1(Paint().apply {
|
||||
color = Color.argb(40, 128, 128, 128)
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
})
|
||||
|
||||
return scaleDisk
|
||||
}
|
||||
|
||||
private fun moveCameraToPosition(geoPoint: GeoPoint) {
|
||||
binding!!.map.controller.animateTo(geoPoint)
|
||||
}
|
||||
|
|
@ -2772,14 +2853,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
|
||||
R.drawable.ic_directions_black_24dp -> {
|
||||
selectedPlace?.let {
|
||||
Utils.handleGeoCoordinates(this.context, it.getLocation())
|
||||
handleGeoCoordinates(requireContext(), it.getLocation())
|
||||
binding?.map?.zoomLevelDouble ?: 0.0
|
||||
}
|
||||
}
|
||||
|
||||
R.drawable.ic_wikidata_logo_24dp -> {
|
||||
selectedPlace?.siteLinks?.wikidataLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2797,13 +2878,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
|
|||
|
||||
R.drawable.ic_wikipedia_logo_24dp -> {
|
||||
selectedPlace?.siteLinks?.wikipediaLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
R.drawable.ic_commons_icon_vector -> {
|
||||
selectedPlace?.siteLinks?.commonsLink?.let {
|
||||
Utils.handleWebUrl(this.context, it)
|
||||
handleWebUrl(requireContext(), it)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.google.android.material.snackbar.Snackbar
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
import fr.free.nrw.commons.databinding.ActivityNotificationBinding
|
||||
|
|
@ -22,6 +21,7 @@ import fr.free.nrw.commons.notification.models.NotificationType
|
|||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.NetworkUtils
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
|
@ -197,7 +197,7 @@ class NotificationActivity : BaseActivity() {
|
|||
|
||||
private fun handleUrl(url: String?) {
|
||||
if (url.isNullOrEmpty()) return
|
||||
Utils.handleWebUrl(this, Uri.parse(url))
|
||||
handleWebUrl(this, Uri.parse(url))
|
||||
}
|
||||
|
||||
private fun setItems(notificationList: List<Notification>?) {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,17 @@ package fr.free.nrw.commons.profile
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.ViewPagerAdapter
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment
|
||||
|
|
@ -23,7 +24,7 @@ import fr.free.nrw.commons.theme.BaseActivity
|
|||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -71,7 +72,7 @@ class ProfileActivity : BaseActivity() {
|
|||
title = userName
|
||||
shouldShowContributions = intent.getBooleanExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, false)
|
||||
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
setTabs()
|
||||
|
|
@ -83,39 +84,23 @@ class ProfileActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun setTabs() {
|
||||
val fragmentList = mutableListOf<Fragment>()
|
||||
val titleList = mutableListOf<String>()
|
||||
|
||||
// Add Achievements tab
|
||||
achievementsFragment = AchievementsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
fragmentList.add(achievementsFragment)
|
||||
titleList.add(resources.getString(R.string.achievements_tab_title).uppercase())
|
||||
|
||||
// Add Leaderboard tab
|
||||
leaderboardFragment = LeaderboardFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
fragmentList.add(leaderboardFragment)
|
||||
titleList.add(resources.getString(R.string.leaderboard_tab_title).uppercase(Locale.ROOT))
|
||||
|
||||
// Add Contributions tab
|
||||
contributionsFragment = ContributionsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
}
|
||||
contributionsFragment?.let {
|
||||
fragmentList.add(it)
|
||||
titleList.add(getString(R.string.contributions_fragment).uppercase(Locale.ROOT))
|
||||
arguments = bundleOf(KEY_USERNAME to userName)
|
||||
}
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList)
|
||||
viewPagerAdapter.setTabs(
|
||||
R.string.achievements_tab_title to achievementsFragment,
|
||||
R.string.leaderboard_tab_title to leaderboardFragment,
|
||||
R.string.contributions_fragment to contributionsFragment!!
|
||||
)
|
||||
viewPagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +118,7 @@ class ProfileActivity : BaseActivity() {
|
|||
return when (item.itemId) {
|
||||
R.id.share_app_icon -> {
|
||||
val rootView = window.decorView.findViewById<View>(android.R.id.content)
|
||||
val screenShot = Utils.getScreenShot(rootView)
|
||||
val screenShot = getScreenShot(rootView)
|
||||
if (screenShot == null) {
|
||||
Log.e("ERROR", "ScreenShot is null")
|
||||
return false
|
||||
|
|
@ -212,6 +197,24 @@ class ProfileActivity : BaseActivity() {
|
|||
binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* To take screenshot of the screen and return it in Bitmap format
|
||||
*
|
||||
* @param view
|
||||
* @return
|
||||
*/
|
||||
fun getScreenShot(view: View): Bitmap? {
|
||||
val screenView = view.rootView
|
||||
screenView.isDrawingCacheEnabled = true
|
||||
val drawingCache = screenView.drawingCache
|
||||
if (drawingCache != null) {
|
||||
val bitmap = Bitmap.createBitmap(drawingCache)
|
||||
screenView.isDrawingCacheEnabled = false
|
||||
return bitmap
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_USERNAME = "username"
|
||||
const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import com.google.android.material.badge.BadgeDrawable
|
|||
import com.google.android.material.badge.BadgeUtils
|
||||
import com.google.android.material.badge.ExperimentalBadgeUtils
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.databinding.FragmentAchievementsBinding
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
|
||||
|
|
@ -27,6 +26,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
|||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showLongToast
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
|
@ -524,7 +524,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
|
|||
getString(R.string.ok),
|
||||
getString(R.string.read_help_link),
|
||||
{},
|
||||
{ Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
|
||||
{ handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
|
||||
null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport
|
|||
import com.karumi.dexter.PermissionToken
|
||||
import com.karumi.dexter.listener.PermissionRequest
|
||||
import com.karumi.dexter.listener.multi.MultiplePermissionsListener
|
||||
import fr.free.nrw.commons.BuildConfig.MOBILE_META_URL
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.activity.SingleWebViewActivity
|
||||
import fr.free.nrw.commons.campaigns.CampaignView
|
||||
import fr.free.nrw.commons.contributions.ContributionController
|
||||
|
|
@ -53,6 +51,7 @@ import fr.free.nrw.commons.utils.DialogUtil
|
|||
import fr.free.nrw.commons.utils.PermissionUtils
|
||||
import fr.free.nrw.commons.utils.StringUtil
|
||||
import fr.free.nrw.commons.utils.ViewUtil
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
|
@ -239,7 +238,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
val betaTesterPreference: Preference? = findPreference("becomeBetaTester")
|
||||
betaTesterPreference?.setOnPreferenceClickListener {
|
||||
Utils.handleWebUrl(requireActivity(), Uri.parse(getString(R.string.beta_opt_in_link)))
|
||||
handleWebUrl(
|
||||
requireActivity(),
|
||||
Uri.parse(getString(R.string.beta_opt_in_link))
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +298,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
getString(R.string.ok),
|
||||
getString(R.string.read_help_link),
|
||||
{ },
|
||||
{ Utils.handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) },
|
||||
{ handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) },
|
||||
null
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.content.Context
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.contributions.Contribution
|
||||
import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource
|
||||
import fr.free.nrw.commons.settings.Prefs.Licenses
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
|
||||
import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
|
|
@ -49,7 +49,7 @@ class PageContentsCreator @Inject constructor(private val context: Context) {
|
|||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"{{Wiki Loves Monuments %d|1= %s}}\n",
|
||||
Utils.getWikiLovesMonumentsYear(Calendar.getInstance()),
|
||||
getWikiLovesMonumentsYear(Calendar.getInstance()),
|
||||
contribution.countryCode
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.net.Uri
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType
|
||||
import fr.free.nrw.commons.nearby.Place
|
||||
import fr.free.nrw.commons.utils.ImageUtils
|
||||
import fr.free.nrw.commons.utils.fixExtension
|
||||
|
||||
class UploadItem(
|
||||
var mediaUri: Uri?,
|
||||
|
|
@ -32,7 +32,7 @@ class UploadItem(
|
|||
* languages have been entered, the first language is used.
|
||||
*/
|
||||
val filename: String
|
||||
get() = Utils.fixExtension(
|
||||
get() = fixExtension(
|
||||
uploadMediaDetails[0].captionText,
|
||||
getExtensionFromMimeType(mimeType)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ class UploadProgressActivity : BaseActivity() {
|
|||
@Inject
|
||||
lateinit var contributionDao: ContributionDao
|
||||
|
||||
val fragmentList: MutableList<Fragment> = ArrayList()
|
||||
val titleList: MutableList<String> = ArrayList()
|
||||
var isPaused = true
|
||||
var isPendingIconsVisible = true
|
||||
var isErrorIconsVisisble = false
|
||||
|
|
@ -38,7 +36,7 @@ class UploadProgressActivity : BaseActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityUploadProgressBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
viewPagerAdapter = ViewPagerAdapter(this, supportFragmentManager)
|
||||
binding.uploadProgressViewPager.setAdapter(viewPagerAdapter)
|
||||
binding.uploadProgressViewPager.setId(R.id.upload_progress_view_pager)
|
||||
binding.uploadProgressTabLayout.setupWithViewPager(binding.uploadProgressViewPager)
|
||||
|
|
@ -81,11 +79,10 @@ class UploadProgressActivity : BaseActivity() {
|
|||
pendingUploadsFragment = PendingUploadsFragment()
|
||||
failedUploadsFragment = FailedUploadsFragment()
|
||||
|
||||
fragmentList.add(pendingUploadsFragment!!)
|
||||
titleList.add(getString(R.string.pending))
|
||||
fragmentList.add(failedUploadsFragment!!)
|
||||
titleList.add(getString(R.string.failed))
|
||||
viewPagerAdapter!!.setTabData(fragmentList, titleList)
|
||||
viewPagerAdapter!!.setTabs(
|
||||
R.string.pending to pendingUploadsFragment!!,
|
||||
R.string.failed to failedUploadsFragment!!
|
||||
)
|
||||
viewPagerAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,11 +16,13 @@ import android.widget.AdapterView
|
|||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding
|
||||
import fr.free.nrw.commons.upload.UploadActivity
|
||||
import fr.free.nrw.commons.upload.UploadBaseFragment
|
||||
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
|
||||
import fr.free.nrw.commons.utils.handleWebUrl
|
||||
import fr.free.nrw.commons.utils.toLicenseName
|
||||
import fr.free.nrw.commons.utils.toLicenseUrl
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -126,20 +128,20 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
|
|||
}
|
||||
|
||||
override fun setSelectedLicense(license: String?) {
|
||||
var position = licenses!!.indexOf(getString(Utils.licenseNameFor(license)))
|
||||
var position = license?.let { licenses!!.indexOf(getString(it.toLicenseName())) } ?: -1
|
||||
// Check if position is valid
|
||||
if (position < 0) {
|
||||
Timber.d("Invalid position: %d. Using default licenses", position)
|
||||
position = licenses!!.size - 1
|
||||
} else {
|
||||
Timber.d("Position: %d %s", position, getString(Utils.licenseNameFor(license)))
|
||||
}
|
||||
binding.spinnerLicenseList.setSelection(position)
|
||||
}
|
||||
|
||||
override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) {
|
||||
val licenseHyperLink = "<a href='" + Utils.licenseUrlFor(selectedLicense) + "'>" +
|
||||
getString(Utils.licenseNameFor(selectedLicense)) + "</a><br>"
|
||||
if (selectedLicense == null) return
|
||||
|
||||
val licenseHyperLink = "<a href='" + selectedLicense.toLicenseUrl() + "'>" +
|
||||
getString(selectedLicense.toLicenseName()) + "</a><br>"
|
||||
|
||||
setTextViewHTML(
|
||||
binding.tvShareLicenseSummary, resources
|
||||
|
|
@ -184,7 +186,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
|
|||
}
|
||||
|
||||
private fun launchBrowser(hyperLink: String) =
|
||||
Utils.handleWebUrl(context, Uri.parse(hyperLink))
|
||||
handleWebUrl(requireContext(), Uri.parse(hyperLink))
|
||||
|
||||
override fun onDestroyView() {
|
||||
presenter.onDetachView()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package fr.free.nrw.commons.upload.license
|
||||
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
import fr.free.nrw.commons.utils.toLicenseName
|
||||
import timber.log.Timber
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
|
@ -34,12 +34,14 @@ class MediaLicensePresenter @Inject constructor(
|
|||
val licenses = repository.getLicenses()
|
||||
view.setLicenses(licenses)
|
||||
|
||||
var selectedLicense = defaultKVStore.getString(
|
||||
//CC_BY_SA_4 is the default one used by the commons web app
|
||||
var selectedLicense: String = defaultKVStore.getString(
|
||||
Prefs.DEFAULT_LICENSE,
|
||||
Prefs.Licenses.CC_BY_SA_4
|
||||
) //CC_BY_SA_4 is the default one used by the commons web app
|
||||
) ?: Prefs.Licenses.CC_BY_SA_4
|
||||
|
||||
try { //I have to make sure that the stored default license was not one of the deprecated one's
|
||||
Utils.licenseNameFor(selectedLicense)
|
||||
selectedLicense.toLicenseName()
|
||||
} catch (exception: IllegalStateException) {
|
||||
Timber.e(exception)
|
||||
selectedLicense = Prefs.Licenses.CC_BY_SA_4
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ interface UploadMediaDetailsContract {
|
|||
fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem)
|
||||
}
|
||||
|
||||
interface UserActionListener : BasePresenter<View?> {
|
||||
interface UserActionListener : BasePresenter<View> {
|
||||
fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore)
|
||||
|
||||
fun receiveImage(
|
||||
|
|
|
|||
|
|
@ -107,7 +107,10 @@ class UploadMediaPresenter @Inject constructor(
|
|||
view.showProgress(false)
|
||||
val gpsCoords = uploadItem.gpsCoords
|
||||
val hasImageCoordinates = gpsCoords != null && gpsCoords.imageCoordsExists
|
||||
if (hasImageCoordinates && place == null) {
|
||||
|
||||
// Only check for nearby places if image has coordinates AND no place was pre-selected
|
||||
// This prevents the popup from appearing when uploading from Nearby feature
|
||||
if (hasImageCoordinates && place == null && uploadItem.place == null) {
|
||||
checkNearbyPlaces(uploadItem)
|
||||
}
|
||||
}, { throwable: Throwable? ->
|
||||
|
|
|
|||
|
|
@ -472,7 +472,10 @@ class UploadWorker(
|
|||
if (wikiDataPlace != null) {
|
||||
if (!contribution.hasInvalidLocation()) {
|
||||
var revisionID: Long? = null
|
||||
val p18WasSkipped = !wikiDataPlace.imageValue.isNullOrBlank()
|
||||
try {
|
||||
if (!p18WasSkipped) {
|
||||
// Only set P18 if the place does not already have a picture
|
||||
revisionID =
|
||||
wikidataEditService.createClaim(
|
||||
wikiDataPlace,
|
||||
|
|
@ -489,9 +492,11 @@ class UploadWorker(
|
|||
.subscribeOn(Schedulers.io())
|
||||
.blockingAwait()
|
||||
Timber.d("Updated WikiItem place ${place.name} with image ${place.pic}")
|
||||
}
|
||||
}
|
||||
showSuccessNotification(contribution)
|
||||
}
|
||||
// Always show success notification, whether P18 was set or skipped
|
||||
showSuccessNotification(contribution)
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception)
|
||||
}
|
||||
|
|
@ -500,6 +505,7 @@ class UploadWorker(
|
|||
wikidataEditService.handleImageClaimResult(
|
||||
contribution.wikidataPlace!!,
|
||||
revisionID,
|
||||
p18WasSkipped = p18WasSkipped
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Context.CLIPBOARD_SERVICE
|
||||
|
||||
object ClipboardUtils {
|
||||
// Convenience for Java usages - remove when they are converted.
|
||||
@JvmStatic
|
||||
fun copy(label: String?, text: String?, context: Context) {
|
||||
context.copyToClipboard(label, text)
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.copyToClipboard(label: String?, text: String?) {
|
||||
with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) {
|
||||
setPrimaryClip(ClipData.newPlainText(label, text))
|
||||
}
|
||||
}
|
||||
38
app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt
Normal file
38
app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
private val jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
/**
|
||||
* Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected
|
||||
* @param theTitle File name
|
||||
* @param ext Correct extension
|
||||
* @return File with correct extension
|
||||
*/
|
||||
fun fixExtension(theTitle: String, ext: String?): String {
|
||||
var result = theTitle
|
||||
var extension = ext
|
||||
|
||||
// People are used to ".jpg" more than ".jpeg" which the system gives us.
|
||||
if (extension != null && extension.lowercase() == "jpeg") {
|
||||
extension = "jpg"
|
||||
}
|
||||
|
||||
result = jpegPattern.matcher(result).replaceFirst(".jpg")
|
||||
if (extension != null &&
|
||||
!result.lowercase(Locale.getDefault()).endsWith("." + extension.lowercase())
|
||||
) {
|
||||
result += ".$extension"
|
||||
}
|
||||
|
||||
// If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228)
|
||||
// If title has an extension in it, if won't be true
|
||||
if (extension == null && result.lastIndexOf(".") <= 0) {
|
||||
extension = "jpg"
|
||||
result += ".$extension"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.utils.ViewUtil.showShortToast
|
||||
|
||||
/**
|
||||
* Util function to handle geo coordinates with specified zoom level. It no longer depends on
|
||||
* google maps and any app capable of handling the map intent can handle it
|
||||
*
|
||||
* @param context The context for launching intent
|
||||
* @param latLng The latitude and longitude of the location
|
||||
* @param zoomLevel The zoom level
|
||||
*/
|
||||
fun handleGeoCoordinates(
|
||||
context: Context, latLng: LatLng,
|
||||
zoomLevel: Double = 16.0
|
||||
) {
|
||||
val mapIntent = Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel))
|
||||
if (mapIntent.resolveActivity(context.packageManager) != null) {
|
||||
context.startActivity(mapIntent)
|
||||
} else {
|
||||
showShortToast(context, context.getString(R.string.map_application_missing))
|
||||
}
|
||||
}
|
||||
31
app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt
Normal file
31
app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
|
||||
/**
|
||||
* Generates licence name with given ID
|
||||
* @return Name of license
|
||||
*/
|
||||
fun String.toLicenseName(): Int = when (this) {
|
||||
Prefs.Licenses.CC_BY_3 -> R.string.license_name_cc_by
|
||||
Prefs.Licenses.CC_BY_4 -> R.string.license_name_cc_by_four
|
||||
Prefs.Licenses.CC_BY_SA_3 -> R.string.license_name_cc_by_sa
|
||||
Prefs.Licenses.CC_BY_SA_4 -> R.string.license_name_cc_by_sa_four
|
||||
Prefs.Licenses.CC0 -> R.string.license_name_cc0
|
||||
else -> throw IllegalStateException("Unrecognized license value: $this")
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates license url with given ID
|
||||
* @return Url of license
|
||||
*/
|
||||
fun String.toLicenseUrl(): String = when (this) {
|
||||
Prefs.Licenses.CC_BY_3 -> "https://creativecommons.org/licenses/by/3.0/"
|
||||
Prefs.Licenses.CC_BY_4 -> "https://creativecommons.org/licenses/by/4.0/"
|
||||
Prefs.Licenses.CC_BY_SA_3 -> "https://creativecommons.org/licenses/by-sa/3.0/"
|
||||
Prefs.Licenses.CC_BY_SA_4 -> "https://creativecommons.org/licenses/by-sa/4.0/"
|
||||
Prefs.Licenses.CC0 -> "https://creativecommons.org/publicdomain/zero/1.0/"
|
||||
else -> throw IllegalStateException("Unrecognized license value: $this")
|
||||
}
|
||||
|
||||
39
app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt
Normal file
39
app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Get the start date of wlm monument
|
||||
* For this release we are hardcoding it to be 1st September
|
||||
* @return
|
||||
*/
|
||||
const val wLMStartDate: String = "1 Sep"
|
||||
|
||||
/***
|
||||
* Get the end date of wlm monument
|
||||
* For this release we are hardcoding it to be 31st October
|
||||
* @return
|
||||
*/
|
||||
const val wLMEndDate: String = "30 Sep"
|
||||
|
||||
/**
|
||||
* For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt
|
||||
*/
|
||||
val isMonumentsEnabled: Boolean
|
||||
get() = Date().month == 8
|
||||
|
||||
/***
|
||||
* Function to get the current WLM year
|
||||
* It increments at the start of September in line with the other WLM functions
|
||||
* (No consideration of locales for now)
|
||||
* @param calendar
|
||||
* @return
|
||||
*/
|
||||
fun getWikiLovesMonumentsYear(calendar: Calendar): Int {
|
||||
var year = calendar[Calendar.YEAR]
|
||||
if (calendar[Calendar.MONTH] < Calendar.SEPTEMBER) {
|
||||
year -= 1
|
||||
}
|
||||
return year
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.underline
|
||||
|
||||
object UnderlineUtils {
|
||||
// Convenience method for Java usages - remove when those classes are converted
|
||||
@JvmStatic
|
||||
fun setUnderlinedText(textView: TextView, stringResourceName: Int) {
|
||||
textView.setUnderlinedText(stringResourceName)
|
||||
}
|
||||
}
|
||||
|
||||
fun TextView.setUnderlinedText(stringResourceName: Int) {
|
||||
text = buildSpannedString {
|
||||
underline { append(context.getString(stringResourceName)) }
|
||||
}
|
||||
}
|
||||
33
app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt
Normal file
33
app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.browser.customtabs.CustomTabColorSchemeParams
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.core.content.ContextCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Opens Custom Tab Activity with in-app browser for the specified URL.
|
||||
* Launches intent for web URL
|
||||
*/
|
||||
fun handleWebUrl(context: Context, url: Uri) {
|
||||
Timber.d("Launching web url %s", url.toString())
|
||||
|
||||
val color = CustomTabColorSchemeParams.Builder()
|
||||
.setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor))
|
||||
.setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor))
|
||||
.build()
|
||||
|
||||
val customTabsIntent = CustomTabsIntent.Builder()
|
||||
.setDefaultColorSchemeParams(color)
|
||||
.setExitAnimations(
|
||||
context, android.R.anim.slide_in_left, android.R.anim.slide_out_right
|
||||
).build()
|
||||
|
||||
// Clear previous browser tasks, so that back/exit buttons work as intended.
|
||||
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
customTabsIntent.launchUrl(context, url)
|
||||
}
|
||||
|
|
@ -196,13 +196,16 @@ class WikidataEditService @Inject constructor(
|
|||
return wikidataClient.setClaim(claim, COMMONS_APP_TAG).blockingSingle()
|
||||
}
|
||||
|
||||
fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?) {
|
||||
fun handleImageClaimResult(wikidataItem: WikidataItem, revisionId: Long?, p18WasSkipped: Boolean = false) {
|
||||
if (revisionId != null) {
|
||||
wikidataEditListener?.onSuccessfulWikidataEdit()
|
||||
showSuccessToast(wikidataItem.name)
|
||||
} else {
|
||||
} else if (!p18WasSkipped) {
|
||||
Timber.d("Unable to make wiki data edit for entity %s", wikidataItem)
|
||||
showLongToast(context, context.getString(R.string.wikidata_edit_failure))
|
||||
} else {
|
||||
Timber.d("Wikidata edit skipped for entity %s because P18 already exists", wikidataItem)
|
||||
// No error shown to user, as this is not a failure
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Ek het besef dat dit sleg is vir my privaatheid</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Ek het van plan verander, ek wil nie hê dit moet meer in die openbaar sigbaar wees nie</string>
|
||||
<string name="deletion_reason_not_interesting">Jammer, hierdie foto is nie interessant vir \'n ensiklopedie nie</string>
|
||||
<string name="uploaded_by_myself">Opgelaai deur myself op %1$s, gebruik in %2$d artikel(s).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Opgelaai deur myself op %1$s, gebruik in %2$d artikel(s).</string>
|
||||
<string name="no_uploads">Welkom by Commons!\n\nLaai u eerste media op deur op die byvoegknoppie te tik.</string>
|
||||
<string name="no_categories_selected">Geen kategorieë gekies nie</string>
|
||||
<string name="no_categories_selected_warning_desc" fuzzy="true">Beelde sonder kategorieë is selde bruikbaar. Is u seker dat u wil indien sonder om kategorieë te kies?</string>
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">أدركت أنه سيئ لخصوصيتي</string>
|
||||
<string name="deletion_reason_no_longer_want_public">لقد غيرت رأيي، ولا أريد أن يكون مرئيا للجميع بعد الآن</string>
|
||||
<string name="deletion_reason_not_interesting">آسف هذه الصورة ليست مثيرة للاهتمام لموسوعة</string>
|
||||
<string name="uploaded_by_myself">رفعته بنفسي في %1$s، مستخدم في %2$d مقالة(ات).</string>
|
||||
<string name="uploaded_by_myself">تم تحميله بواسطة نفسي على %1$s، وتم استخدامه في %2$d مقالة على الأقل.</string>
|
||||
<string name="no_uploads">مرحبا بك في كومنز! \n\nارفع الوسائط الأولى عن طريق النقر على زر الإضافة.</string>
|
||||
<string name="no_categories_selected">لا توجد تصنيفات مختارة</string>
|
||||
<string name="no_categories_selected_warning_desc">نادرًا ما تكون الصور الخالية من التصنيفات قابلة للاستخدام، هل أنت متأكد أنك تريد الإرسال دون تحديد التصنيفات؟</string>
|
||||
|
|
@ -893,4 +893,5 @@
|
|||
<string name="show_in_nearby">عرض في المناطق القريبة</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">تم الإنشاء والتحميل بواسطة: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">تم إنشاؤه بواسطة %1$s وتم تحميله بواسطة %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">مرشح للحذف</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Decatéme que ye malo pa la mio privacidá</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Cambié d\'opinión, nun quiero que siga siendo visible públicamente</string>
|
||||
<string name="deletion_reason_not_interesting">Sentímoslo, esta imaxe nun ye interesante pa una enciclopedia</string>
|
||||
<string name="uploaded_by_myself">Xubíla yo mesmu\'l %1$s, usada en %2$d artículu(os).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Xubíla yo mesmu\'l %1$s, usada en %2$d artículu(os).</string>
|
||||
<string name="no_uploads">¡Afáyate\'n Commons.\n\nCarga\'l to primer ficheru tocando nel botón amestar.</string>
|
||||
<string name="no_categories_selected">Nun hai categoríes seleicionaes</string>
|
||||
<string name="no_categories_selected_warning_desc">Les imáxenes ensin categoríes raramente puen usase. ¿Seguro que quies siguir ensin escoyer categoría dala?</string>
|
||||
|
|
|
|||
|
|
@ -44,30 +44,63 @@
|
|||
<item quantity="other">(%1$d)</item>
|
||||
</plurals>
|
||||
<string name="starting_uploads">Yükləmələrə Başlanılır</string>
|
||||
<plurals name="starting_multiple_uploads">
|
||||
<item quantity="one">%d yükləmə emal edilir</item>
|
||||
<item quantity="other">%d yükləmə emal edilir</item>
|
||||
</plurals>
|
||||
<plurals name="multiple_uploads_title">
|
||||
<item quantity="one">%d yükləmə</item>
|
||||
<item quantity="other">%d yükləmə</item>
|
||||
</plurals>
|
||||
<plurals name="share_license_summary">
|
||||
<item quantity="one">Bu şəkil %1$s lisenziyası altında yayımlanacaq</item>
|
||||
<item quantity="other">Bu şəkillər %1$s lisenziyası altında yayımlanacaq</item>
|
||||
</plurals>
|
||||
<plurals name="upload_count_title">
|
||||
<item quantity="one">%1$d yükləmə</item>
|
||||
<item quantity="other">%1$d yükləmə</item>
|
||||
</plurals>
|
||||
<plurals name="receiving_shared_content">
|
||||
<item quantity="one">Paylaşılan məzmun qəbul edilir. Şəklin emalı şəklin ölçüsündən və cihazınızdan asılı olaraq bir az vaxt apara bilər</item>
|
||||
<item quantity="other">Paylaşılan məzmun qəbul edilir. Şəkillərin emalı şəklin ölçüsündən və cihazınızdan asılı olaraq bir az vaxt apara bilər</item>
|
||||
</plurals>
|
||||
<string name="navigation_item_explore">Kəşf et</string>
|
||||
<string name="preference_category_appearance">Görünüş</string>
|
||||
<string name="preference_category_general">Ümumi</string>
|
||||
<string name="preference_category_feedback">Rəy</string>
|
||||
<string name="preference_category_privacy">Məxfilik</string>
|
||||
<string name="app_name">Vikianbar</string>
|
||||
<string name="menu_settings">Tənzimləmələr</string>
|
||||
<string name="intent_share_upload_label">Vikianbara yüklə</string>
|
||||
<string name="upload_in_progress">Yükləmə davam edir</string>
|
||||
<string name="username">İstifadəçi adı</string>
|
||||
<string name="password">Parol</string>
|
||||
<string name="login_credential">Commons Beta hesabınıza daxil olun</string>
|
||||
<string name="login">Daxil ol</string>
|
||||
<string name="forgot_password">Şifrəni unutmusunuz?</string>
|
||||
<string name="signup">Qeydiyyatdan keç</string>
|
||||
<string name="logging_in_title">Giriş edilir</string>
|
||||
<string name="logging_in_message">Zəhmət olmasa, gözləyin…</string>
|
||||
<string name="updating_caption_title">Başlıqların və təsvirlərin yenilənməsi</string>
|
||||
<string name="updating_caption_message">Zəhmət olmasa, gözləyin…</string>
|
||||
<string name="login_success">Uğurlu giriş!</string>
|
||||
<string name="login_failed">Giriş baş tutmadı!</string>
|
||||
<string name="upload_failed">Fayl tapılmadı. Xahiş edirik başqa bir fayl üzərində cəhd edin.</string>
|
||||
<string name="retry_limit_reached">Maksimum təkrar cəhd limitinə çatdınız! Zəhmət olmasa, yükləməni ləğv edin və yenidən cəhd edin</string>
|
||||
<string name="unrestricted_battery_mode">Batareya optimallaşdırılması söndürülsün?</string>
|
||||
<string name="authentication_failed" fuzzy="true">Doğrulama alınmadı, xahiş edirəm yenidən daxil olun</string>
|
||||
<string name="suggest_unrestricted_mode">3-dən çox şəklin yüklənməsi batareyanın optimallaşdırılması söndürüldükdə daha yaxşı işləyir. Rahat yükləmə təcrübəsi üçün Vikianbar tətbiqi üçün batareyanın optimallaşdırılmasını parametrlərdən söndürün. \n\nBatareyanın optimallaşdırılmasını söndürmək üçün mümkün addımlar:\n\nAddım 1: Aşağıdakı \"Parametrlər\" düyməsinə toxunun.\n\nAddım 2: \"Optimallaşdırılmayıb\"dan \"Bütün proqramlar\"a keçin.\n\nAddım 3: \"Commons\" və ya \"fr.free.nrw.commons\" axtarın.\n\nAddım 4: \"Optimallaşdırma\"nı seçin.\n\nAddım 5: \'Bitti\' düyməsini basın.</string>
|
||||
<string name="authentication_failed">Doğrulama alınmadı. Zəhmət olmasa, yenidən daxil olun</string>
|
||||
<string name="uploading_started">Yükləmə başladı!</string>
|
||||
<string name="uploading_queued">Yükləmə növbədə (məhdud əlaqə rejimi aktivdir)</string>
|
||||
<string name="upload_completed_notification_title">%1$s yükləndi!</string>
|
||||
<string name="upload_completed_notification_text">Yükləmənizə baxmaq üçün toxunun</string>
|
||||
<string name="upload_progress_notification_title_start" fuzzy="true">%1$s yüklənməsi başlanır</string>
|
||||
<string name="upload_progress_notification_title_start">Fayl yüklənir: %s</string>
|
||||
<string name="upload_progress_notification_title_in_progress">%1$s yüklənir</string>
|
||||
<string name="upload_progress_notification_title_finishing">%1$s yüklənməsi başa çatdı</string>
|
||||
<string name="upload_failed_notification_title" fuzzy="true">%1$s faylının yüklənməsi alınmadı</string>
|
||||
<string name="upload_failed_notification_title">%1$s yükləmək alınmadı</string>
|
||||
<string name="upload_paused_notification_title">%1$s yüklənməsi dayandırıldı</string>
|
||||
<string name="upload_failed_notification_subtitle">Baxmaq üçün toxun</string>
|
||||
<string name="upload_paused_notification_subtitle">Baxmaq üçün toxun</string>
|
||||
<string name="title_activity_contributions">Son Yükləmələrim</string>
|
||||
<string name="contribution_state_queued">Növbəyə alındı</string>
|
||||
<string name="contribution_state_failed">Uğursuz</string>
|
||||
|
|
@ -77,49 +110,291 @@
|
|||
<string name="menu_from_camera">Foto çək</string>
|
||||
<string name="menu_nearby">Yaxınlıqdakılar</string>
|
||||
<string name="provider_contributions">Yükləmələrim</string>
|
||||
<string name="menu_copy_link">Keçidi kopiyala</string>
|
||||
<string name="menu_link_copied">Link kopyalandı.</string>
|
||||
<string name="menu_share">Paylaş</string>
|
||||
<string name="menu_view_file_page">Faylın səhifəsinə bax</string>
|
||||
<string name="share_title_hint">Başlıq (Zəruri)</string>
|
||||
<string name="add_caption_toast">Zəhmət olmasa, bu fayl üçün başlıq təqdim et</string>
|
||||
<string name="share_description_hint">Açıqlama</string>
|
||||
<string name="login_failed_network" fuzzy="true">Daxil olmaq olmur — şəbəkə xətası</string>
|
||||
<string name="share_caption_hint">Başlıq</string>
|
||||
<string name="login_failed_network">Daxil olmaq mümkün deyil - şəbəkə xətası</string>
|
||||
<string name="login_failed_throttled">Çox sayda uğursuz daxil olma. Xahiş edirik bir neçə dəqiqə sonra yenidən cəhd edin.</string>
|
||||
<string name="login_failed_blocked">Bağışlayın, bu istifadəçi Vikianbardan bloklanıb</string>
|
||||
<string name="login_failed_2fa_needed">Siz iki faktorlu autentifikasiya kodunuzu təqdim etməlisiniz.</string>
|
||||
<string name="login_failed_generic" fuzzy="true">Daxil olma uğursuz oldu</string>
|
||||
<string name="login_failed_email_auth_needed">E-poçt ünvanınıza giriş doğrulama kodu göndərildi. Daxil olmaq üçün kodu daxil edin.</string>
|
||||
<string name="login_failed_generic">Daxil olma uğursuz oldu</string>
|
||||
<string name="share_upload_button">Yüklə</string>
|
||||
<string name="multiple_share_base_title">Bu dəsti adlandırın</string>
|
||||
<string name="provider_modifications">Dəyişikliklər</string>
|
||||
<string name="menu_upload_single">Yüklə</string>
|
||||
<string name="categories_search_text_hint">Kateqoriyalarda axtar</string>
|
||||
<string name="depicts_search_text_hint">Medianızın təsvir etdiyi elementləri axtarın (dağ, Tac Mahal və s.)</string>
|
||||
<string name="menu_save_categories">Yadda saxla</string>
|
||||
<string name="refresh_button">Yenilə</string>
|
||||
<string name="display_list_button">Siyahı</string>
|
||||
<string name="contributions_subtitle_zero">(Hələ yükləmə yoxdur)</string>
|
||||
<string name="categories_not_found">%1$s axtarışına uyğun kateqoriya tapılmadı</string>
|
||||
<string name="depictions_not_found">%1$s uyğun heç bir Vikidata elementi tapılmadı</string>
|
||||
<string name="no_child_classes">%1$s alt sinifləri yoxdur</string>
|
||||
<string name="no_parent_classes">%1$s üst sinfi yoxdur</string>
|
||||
<string name="categories_skip_explanation">Şəkillərinizi Vikianbarda daha tapıla bilən etmək üçün kateqoriyalar əlavə edin.\nKateqoriyalar əlavə etmək üçün yazmağa başlayın.</string>
|
||||
<string name="categories_activity_title">Kateqoriyalar</string>
|
||||
<string name="title_activity_settings">Nizamlamalar</string>
|
||||
<string name="title_activity_signup">Qeydiyyatdan keç</string>
|
||||
<string name="title_activity_featured_images">Seçilmiş şəkillər</string>
|
||||
<string name="title_activity_custom_selector">Xüsusi Seçici</string>
|
||||
<string name="title_activity_category_details">Kateqoriya</string>
|
||||
<string name="title_activity_review">Yoxlanış</string>
|
||||
<string name="menu_about">Haqqında</string>
|
||||
<string name="about_license">Vikianbar proqramı Vikimedia icmasının qrant alanları və könüllüləri tərəfindən yaradılmış və dəstəklənən açıq mənbəli proqramdır. Vikimedia Fondu proqramın yaradılması, inkişafı və ya texniki xidmətində iştirak etmir.</string>
|
||||
<string name="about_improve">Xəta hesabatları və təkliflər üçün yeni <a href=\"%1$s\">GitHub sorğusu</a> yaradın.</string>
|
||||
<string name="about_privacy_policy">Məxfilik siyasəti</string>
|
||||
<string name="about_credits">Töhfə verənlər</string>
|
||||
<string name="title_activity_about">Haqqında</string>
|
||||
<string name="menu_feedback">Rəy göndər (e-poçt vasitəsilə)</string>
|
||||
<string name="no_email_client">Heç bir e-poçt tətbiqi quraşdırılmayıb</string>
|
||||
<string name="provider_categories">Son istifadə olunan kateqoriyalar</string>
|
||||
<string name="waiting_first_sync">İlk sinxronizasiya gözlənilir…</string>
|
||||
<string name="no_uploads_yet">Siz hələ heç bir şəkil yükləməmisiniz.</string>
|
||||
<string name="menu_retry_upload">Yenidən cəhd edin</string>
|
||||
<string name="menu_cancel_upload">İmtina</string>
|
||||
<string name="media_upload_policy">Bu şəkli yükləməklə bildirirəm ki, bu mənim şəxsi işimdir, onda müəllif hüququ ilə qorunan material və ya selfilər yoxdur və <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Vikianbar qaydalarına</a> riayət edir.</string>
|
||||
<string name="menu_download">Endir</string>
|
||||
<string name="preference_license">Defolt lisenziya</string>
|
||||
<string name="use_previous">Əvvəlki başlıq və təsvirdən istifadə et</string>
|
||||
<string name="preference_theme">Tema</string>
|
||||
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
|
||||
<string name="tutorial_1_text">Vikianbar Vikipediyada istifadə olunan şəkillərin əksəriyyətinə ev sahibliyi edir.</string>
|
||||
<string name="tutorial_1_subtext">Şəkilləriniz bütün dünyada insanları maarifləndirməyə kömək edir!</string>
|
||||
<string name="tutorial_2_text">Tamamilə özünüz tərəfindən çəkilmiş və ya yaradılmış şəkilləri yükləyin:</string>
|
||||
<string name="tutorial_2_subtext_1">Təbii obyektlər (çiçəklər, heyvanlar, dağlar)</string>
|
||||
<string name="tutorial_2_subtext_2">Faydalı obyektlər (velosipedlər, qatar stansiyaları)</string>
|
||||
<string name="tutorial_2_subtext_3">Məşhur insanlar (icra başçınız, tanış olduğunuz olimpiya idmançıları)</string>
|
||||
<string name="tutorial_3_text">Zəhmət olmasa, YÜKLƏMƏYİN:</string>
|
||||
<string name="tutorial_3_subtext_1">Selfi və ya dostlarınızın şəkilləri</string>
|
||||
<string name="tutorial_3_subtext_2">İnternetdə tapdığınız şəkillər</string>
|
||||
<string name="tutorial_3_subtext_3">Özəl proqramların ekran görüntüləri</string>
|
||||
<string name="tutorial_4_text">Yükləmə nümunəsi:</string>
|
||||
<string name="tutorial_4_subtext_1">Başlıq: Sidney Opera Evi</string>
|
||||
<string name="tutorial_4_subtext_2">Təsvir: Sidney Opera Evinin körfəzdən görünüşü</string>
|
||||
<string name="tutorial_4_subtext_3">Kateqoriyalar: Sidney Opera Evi Qərb istiqamətindən, Sidney Opera Evinin uzaqdan mənzərələri</string>
|
||||
<string name="welcome_wikipedia_text">Şəkilləriniz ilə töhfə verin. Vikipediya məqalələrinin canlanmasına kömək edin!</string>
|
||||
<string name="welcome_wikipedia_subtext">Vikipediyadakı şəkillər Vikianbardan götürülüb.</string>
|
||||
<string name="welcome_copyright_text">Şəkilləriniz bütün dünyada insanları maarifləndirməyə kömək edir.</string>
|
||||
<string name="welcome_copyright_subtext">İnternetdən tapdığınız müəllif hüquqları ilə qorunan materialları, eləcə də afişaların, kitab üzlüklərinin və s. şəkillərini yükləməkdən çəkinin.</string>
|
||||
<string name="welcome_final_text">Sizcə bunu başa düşdünüz?</string>
|
||||
<string name="welcome_final_button_text">Bəli!</string>
|
||||
<string name="welcome_help_button_text">Əlavə məlumat</string>
|
||||
<string name="detail_panel_cats_label">Kateqoriyalar</string>
|
||||
<string name="detail_panel_cats_loading">Yüklənir...</string>
|
||||
<string name="detail_panel_cats_none">Heç biri seçilməmişdir</string>
|
||||
<string name="detail_caption_empty">Başlıq yoxdur</string>
|
||||
<string name="detail_description_empty">Təsvir yoxdur</string>
|
||||
<string name="detail_discussion_empty">Müzakirə yoxdur</string>
|
||||
<string name="detail_license_empty">Naməlum lisenziya</string>
|
||||
<string name="menu_refresh">Yenilə</string>
|
||||
<string name="storage_permission_title">Yaddaşa giriş icazəsi tələb olunur</string>
|
||||
<string name="read_storage_permission_rationale">Tələb olunan icazə: Xarici yaddaşı oxumaq. Bu olmadan proqram qalereyanıza daxil ola bilməz.</string>
|
||||
<string name="write_storage_permission_rationale">Tələb olunan icazə: Xarici yaddaşa yazmaq. Bu olmadan proqram kameranıza/qalereyanıza daxil ola bilməz.</string>
|
||||
<string name="location_permission_title">Məkan icazəsi tələb olunur</string>
|
||||
<string name="in_app_camera_location_permission_title">Tətbiqdaxili çəkilişlər üçün məkanı qeyd et</string>
|
||||
<string name="in_app_camera_location_switch_pref_summary">Cihaz kamerası onu qeyd etmədiyi halda, məkanı tətbiqdaxili çəkilişlərlə əlavə etmək üçün bunu aktiv edin</string>
|
||||
<string name="ok">Oldu</string>
|
||||
<string name="warning">Xəbərdarlıq</string>
|
||||
<string name="duplicate_file_name">Dublikat fayl adı tapıldı</string>
|
||||
<string name="upload">Yüklə</string>
|
||||
<string name="yes">Bəli</string>
|
||||
<string name="no">Xeyr</string>
|
||||
<string name="media_detail_caption">Başlıq</string>
|
||||
<string name="media_detail_title">Başlıq</string>
|
||||
<string name="media_detail_depiction">Təsvirlər</string>
|
||||
<string name="media_detail_description">Təsvir</string>
|
||||
<string name="media_detail_discussion">Müzakirə</string>
|
||||
<string name="media_detail_author">Müəllif</string>
|
||||
<string name="media_detail_uploaded_date">Yüklənmə tarixi</string>
|
||||
<string name="media_detail_license">Lisenziya</string>
|
||||
<string name="media_detail_coordinates">Koordinatlar</string>
|
||||
<string name="media_detail_coordinates_empty">Heç biri göstərilməyib</string>
|
||||
<string name="become_a_tester_title">Beta Tester ol</string>
|
||||
<string name="become_a_tester_description">Google Play-də beta kanalımıza qoşulun və yeni funksiyalara və xəta həllərinə erkən giriş əldə edin</string>
|
||||
<string name="_2fa_code">2FA kodu</string>
|
||||
<string name="email_auth_code">E-poçt doğrulama kodu</string>
|
||||
<string name="logout_verification">Həqiqətən çıxış etmək istəyirsiniz?</string>
|
||||
<string name="mediaimage_failed">Media şəkli uğursuz oldu</string>
|
||||
<string name="no_subcategory_found">Heç bir alt kateqoriya tapılmadı</string>
|
||||
<string name="no_parentcategory_found">Heç bir üst kateqoriya tapılmadı</string>
|
||||
<string name="welcome_image_mount_zao">Zao dağı</string>
|
||||
<string name="welcome_image_llamas">Lamalar</string>
|
||||
<string name="welcome_image_rainbow_bridge">Göy qurşağı körpüsü</string>
|
||||
<string name="welcome_image_tulip">Lalə</string>
|
||||
<string name="welcome_image_welcome_wikipedia">Vikipediyaya xoş gəlmisiniz</string>
|
||||
<string name="welcome_image_welcome_copyright">Müəlliflik hüquqlarına xoş gəlmisiniz</string>
|
||||
<string name="welcome_image_sydney_opera_house">Sidney Opera Evi</string>
|
||||
<string name="cancel">İmtina</string>
|
||||
<string name="navigation_drawer_open">Aç</string>
|
||||
<string name="navigation_drawer_close">Bağla</string>
|
||||
<string name="navigation_item_home">Ana səhifə</string>
|
||||
<string name="navigation_item_upload">Yüklə</string>
|
||||
<string name="navigation_item_nearby">Yaxınlıqdakılar</string>
|
||||
<string name="navigation_item_about">Haqqında</string>
|
||||
<string name="navigation_item_settings">Parametrlər</string>
|
||||
<string name="navigation_item_feedback">Rəy</string>
|
||||
<string name="navigation_item_feedback_github">GitHub vasitəsilə rəy</string>
|
||||
<string name="navigation_item_logout">Çıxış</string>
|
||||
<string name="navigation_item_info">Təlimat</string>
|
||||
<string name="navigation_item_notification">Bildirişlər</string>
|
||||
<string name="navigation_item_review">Yoxla</string>
|
||||
<string name="no_description_found">təsvir tapılmadı</string>
|
||||
<string name="nearby_info_menu_commons_article">Commons fayl səhifəsi</string>
|
||||
<string name="nearby_info_menu_wikidata_article">Vikidata elementi</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">Vikipediya məqaləsi</string>
|
||||
<string name="description_info">Zəhmət olmasa, medianı mümkün qədər təsvir edin: Harada çəkilib? Nəyi təsvir edir? Kontekst nədir? Obyektləri və ya şəxsləri təsvir edin. Asanlıqla təxmin edilə bilməyən məlumatları, məsələn, bir mənzərədirsə, günün saatını qeyd edin. Əgər media qeyri-adi bir şey göstərirsə, zəhmət olmasa, onu izah edin.</string>
|
||||
<string name="caption_info">Zəhmət olmasa şəklin qısa təsvirini yazın. İlk başlıq şəkil üçün Başlıq kimi istifadə olunacaq. 255 simvolla məhdudlaşır.</string>
|
||||
<string name="upload_problem_exist">Bu şəkil ilə bağlı potensial problemlər:</string>
|
||||
<string name="upload_problem_image_dark">Şəkil çox qaranlıqdır.</string>
|
||||
<string name="upload_problem_image_blurry">Şəkil bulanıqdır.</string>
|
||||
<string name="upload_problem_image_duplicate">Şəkil artıq Vikianbarda var.</string>
|
||||
<string name="upload_problem_different_geolocation">Bu şəkil başqa yerdə çəkilib.</string>
|
||||
<string name="upload_problem_fbmd">Zəhmət olmasa, yalnız özünüz çəkdiyiniz şəkilləri yükləyin. Başqalarının Facebook hesablarında tapdığınız şəkilləri yükləməyin.</string>
|
||||
<string name="upload_problem_do_you_continue">Hələ də bu şəkli yükləmək istəyirsiniz?</string>
|
||||
<string name="upload_connection_error_alert_title">Bağlantı xətası</string>
|
||||
<string name="upload_connection_error_alert_detail">Yükləmə prosesi aktiv internetə çıxış tələb edir. Şəbəkə bağlantınızı yoxlayın.</string>
|
||||
<string name="upload_problem_image">Şəkildə tapılan problemlər</string>
|
||||
<string name="internet_downloaded">Zəhmət olmasa, yalnız özünüz çəkdiyiniz şəkilləri yükləyin. İnternetdən tapdığınız şəkilləri yükləməyin.</string>
|
||||
<string name="use_external_storage">Tətbiqdaxili kadrları yadda saxla</string>
|
||||
<string name="use_external_storage_summary">Tətbiqdaxili kamera ilə çəkilmiş şəkilləri cihazın yaddaşında saxla</string>
|
||||
<string name="login_to_your_account">Hesabınıza daxil olun</string>
|
||||
<string name="send_log_file">Jurnal faylını göndərin</string>
|
||||
<string name="send_log_file_description">Proqramla bağlı problemlərin aradan qaldırılmasına kömək etmək üçün jurnal faylını tərtibatçılara e-poçt vasitəsilə göndərin. Qeyd: jurnallar potensial olaraq şəxsi məlumatları ehtiva edə bilər</string>
|
||||
<string name="no_web_browser">URL-i açmaq üçün heç bir veb brauzer tapılmadı</string>
|
||||
<string name="null_url">Xəta! URL tapılmadı</string>
|
||||
<string name="nominate_deletion">Silinməyə namizəd göstər</string>
|
||||
<string name="nominated_for_deletion">Bu şəkil silinməyə namizəddir.</string>
|
||||
<string name="nominated_see_more">Ətraflı məlumat üçün veb səhifəsinə bax</string>
|
||||
<string name="skip_login">Ötür</string>
|
||||
<string name="navigation_item_login">Daxil ol</string>
|
||||
<string name="skip_login_title">Daxil olma mərhələsini keçmək istədiyinizə əminsiniz?</string>
|
||||
<string name="skip_login_message">Gələcəkdə şəkilləri yükləmək üçün daxil olmalısınız.</string>
|
||||
<string name="login_alert_message">Bu funksiyadan istifadə etmək üçün zəhmət olmasa daxil olun</string>
|
||||
<string name="copy_wikicode">Vikimətni mübadilə buferinə köçür</string>
|
||||
<string name="wikicode_copied">Vikimətn mübadilə buferinə köçürüldü</string>
|
||||
<string name="nearby_location_not_available">Yaxınlıqdakılar düzgün işləməyə bilər, məkan əlçatan deyil.</string>
|
||||
<string name="nearby_showing_pins_offline">İnternet əlçatan deyil. Yalnız keşlənmiş yerlər göstərilir.</string>
|
||||
<string name="upload_location_access_denied">Məkan girişi rədd edildi. Bu funksiyadan istifadə etmək üçün yerinizi manual təyin edin.</string>
|
||||
<string name="location_permission_rationale_nearby">Yaxınlıqdakı yerlərin siyahısını göstərmək üçün icazə tələb olunur</string>
|
||||
<string name="location_permission_rationale_explore">Yaxınlıqdakı şəkillərin siyahısını göstərmək üçün icazə tələb olunur</string>
|
||||
<string name="nearby_directions">İstiqamətlər</string>
|
||||
<string name="nearby_wikidata">Vikidata</string>
|
||||
<string name="nearby_wikipedia">Vikipediya</string>
|
||||
<string name="nearby_commons">Vikianbar</string>
|
||||
<string name="about_rate_us">Bizi qiymətləndir</string>
|
||||
<string name="about_faq">TSS</string>
|
||||
<string name="user_guide">İstifadəçi təlimatı</string>
|
||||
<string name="welcome_skip_button">Təlimatı ötür</string>
|
||||
<string name="no_internet">İnternet əlçatan deyil</string>
|
||||
<string name="error_notifications">Bildirişləri əldə edərkən xəta baş verdi</string>
|
||||
<string name="error_review">Şəkli nəzərdən keçirmək üçün gətirilərkən xəta baş verdi. Yenidən cəhd etmək üçün yeniləmə düyməsini basın.</string>
|
||||
<string name="no_notifications">Heç bir bildiriş tapılmadı</string>
|
||||
<string name="about_translate">Tərcümə et</string>
|
||||
<string name="about_translate_title">Dillər</string>
|
||||
<string name="about_translate_message">Tərcümələri təqdim etmək istədiyiniz dili seçin</string>
|
||||
<string name="about_translate_proceed">Davam et</string>
|
||||
<string name="about_translate_cancel">İmtina</string>
|
||||
<string name="retry">Yenidən cəhd et</string>
|
||||
<string name="showcase_view_whole_nearby_activity">Bunlar sizə yaxın yerlərdir və onların Vikipediya məqalələrini təsvir etmək üçün şəkillərə ehtiyac duyurlar.\n\n\'BU SAHƏDƏ AXTAR\' düyməsinə klikləməklə, xəritə kilidləyib həmin məkan ətrafında yaxınlıqda axtarış başlaya bilərsiniz.</string>
|
||||
<string name="showcase_view_needs_photo">Bu yerə şəkil lazımdır.</string>
|
||||
<string name="showcase_view_has_photo">Bu yerin artıq şəkli var.</string>
|
||||
<string name="showcase_view_no_longer_exists">Bu yer artıq mövcud deyil.</string>
|
||||
<string name="no_images_found">Şəkil tapılmadı!</string>
|
||||
<string name="error_loading_images">Şəkillər yüklənərkən xəta baş verdi.</string>
|
||||
<string name="image_uploaded_by">%1$s tərəfindən yüklənilib</string>
|
||||
<string name="block_notification_title">Bloklanıb</string>
|
||||
<string name="block_notification">Sizə Vikianbarı redaktə etmək qadağan edilib</string>
|
||||
<string name="app_widget_heading">Günün Şəkli</string>
|
||||
<string name="menu_search_button">Axtar</string>
|
||||
<string name="search_commons">Vikianbarda axtar</string>
|
||||
<string name="title_activity_search">Axtar</string>
|
||||
<string name="search_recent_header">Son axtarışlar:</string>
|
||||
<string name="provider_searches">Bu yaxınlarda axtarılanlar</string>
|
||||
<string name="provider_recent_languages">Son dil sorğuları</string>
|
||||
<string name="error_loading_categories">Kateqoriyalar yüklənərkən xəta baş verdi.</string>
|
||||
<string name="error_loading_depictions">Təsvirləri yükləyərkən xəta baş verdi.</string>
|
||||
<string name="search_tab_title_media">Media</string>
|
||||
<string name="search_tab_title_categories">Kateqoriyalar</string>
|
||||
<string name="search_tab_title_depictions">Elementlər</string>
|
||||
<string name="explore_tab_title_featured">Seçilmiş</string>
|
||||
<string name="explore_tab_title_mobile">Mobil tətbiq vasitəsilə yüklənib</string>
|
||||
<string name="explore_tab_title_map">Xəritə</string>
|
||||
<string name="successful_wikidata_edit">Şəkil Vikidatada %1$s elementinə əlavə edildi</string>
|
||||
<string name="wikidata_edit_failure">Müvafiq Vikidata obyektini yeniləmək alınmadı!</string>
|
||||
<string name="menu_set_wallpaper">Divar kağızı kimi təyin et</string>
|
||||
<string name="wallpaper_set_successfully">Divar kağızı uğurla təyin edildi!</string>
|
||||
<string name="quiz">Sorğu</string>
|
||||
<string name="quiz_question_string">Bu şəkli yükləmək olar?</string>
|
||||
<string name="question">Sual</string>
|
||||
<string name="result">Nəticə</string>
|
||||
<string name="quiz_back_button">Silinməyi tələb edilən şəkilləri yükləməyə davam etsəniz, hesabınız blok olunacaq. Sorğunu bitirmək istədiyinizə əminsiniz?</string>
|
||||
<string name="quiz_alert_message">Yüklədiyiniz şəkillərin %1$s+ ədədi silinib. Silinməyi tələb edilən şəkilləri yükləməyə davam etsəniz, hesabınız blok olunacaq.\n\nTəlimata yenidən baxmaq və sonra hansı növ şəkilləri yükləməli və ya yükləməməli olduğunuzu öyrənməyə kömək etmək üçün testdən keçmək istəyirsiniz?</string>
|
||||
<string name="selfie_answer">Selfielərin o qədər də məlumatlandırıcı dəyəri yoxdur. Haqqınızda Vikipediya məqaləsi yoxdursa, zəhmət olmasa, şəklinizi yükləməyin.</string>
|
||||
<string name="taj_mahal_answer">Abidələrin və mənzərələrin şəkillərini əksər ölkələrdə yükləmək olar. Nəzərə alın ki, xaricdəki müvəqqəti incəsənət abidələri tez-tez müəllif hüquqları ilə qorunur və yükləmək düzgün deyil.</string>
|
||||
<string name="screenshot_answer">Veb saytların skrinşotları törəmə əsərlər hesab edilir və veb-saytdakı müəllif hüququna tabedir. Bunlar müəllifin icazəsindən sonra istifadə edilə bilər. Belə icazə olmadan, onların işi əsasında yaratdığınız hər hansı əsər qanuni olaraq orijinal müəllifə məxsus lisenziyasız nüsxə sayılır.</string>
|
||||
<string name="blurry_image_answer">Vikianbarın məqsədlərindən biri keyfiyyətli şəkillər toplamaqdır. Ona görə də bulanıq şəkillər yüklənməməlidir. Həmişə yaxşı işıqlandırma ilə gözəl şəkillər çəkməyə çalışın.</string>
|
||||
<string name="construction_event_answer">Texnologiya və ya mədəniyyəti təsvir edən şəkillər üçün Vikianbarda həmişə yer var.</string>
|
||||
<string name="congratulatory_message_quiz">Cavabların %1$s ədədi düzgündür. Təbriklər!</string>
|
||||
<string name="warning_for_no_answer">Suala cavab vermək üçün iki variantdan birini seçin</string>
|
||||
<string name="user_not_logged_in">Giriş müddəti bitib. Zəhmət olmasa, yenidən daxil olun.</string>
|
||||
<string name="quiz_result_share_message">Bunu dostlarınızla paylaşın!</string>
|
||||
<string name="continue_message">Davam et</string>
|
||||
<string name="correct">Düzgün Cavab</string>
|
||||
<string name="wrong">Yanlış Cavab</string>
|
||||
<string name="quiz_screenshot_question">Bu skrinşotu yükləmək olar?</string>
|
||||
<string name="share_app_title">Proqramı paylaş</string>
|
||||
<string name="rotate">Fırlat</string>
|
||||
<string name="error_fetching_nearby_places">Yaxınlıqdakı yerləri yükləmək mümkün olmadı</string>
|
||||
<string name="no_pictures_in_this_area">Bu ərazidə şəkillər yoxdur</string>
|
||||
<string name="no_nearby_places_around">Yaxınlıqda yer tapılmadı</string>
|
||||
<string name="error_fetching_nearby_monuments">Yaxınlıqdakı abidələri əldə edərkən xəta baş verdi.</string>
|
||||
<string name="no_recent_searches">Son axtarışlar yoxdur</string>
|
||||
<string name="delete_recent_searches_dialog">Axtarış tarixçəsini silmək istədiyindən əminsiniz?</string>
|
||||
<string name="cancel_upload_dialog">Bu yükləməni ləğv etmək istədiyinizə əminsiniz?</string>
|
||||
<string name="delete_search_dialog">Bu axtarışı silmək istəyirsiniz?</string>
|
||||
<string name="search_history_deleted">Axtarış tarixçəsi silindi</string>
|
||||
<string name="nominate_delete">Silinməyə namizəd göstər</string>
|
||||
<string name="delete">Sil</string>
|
||||
<string name="Achievements">Nailiyyətlər</string>
|
||||
<string name="Profile">Profil</string>
|
||||
<string name="badges">Rozetlər</string>
|
||||
<string name="statistics">Statistika</string>
|
||||
<string name="statistics_thanks">Qəbul edilən təşəkkürlər</string>
|
||||
<string name="statistics_featured">Seçilmiş şəkillər</string>
|
||||
<string name="statistics_wikidata_edits">\"Yaxınlıqdakı yerlər\" vasitəsilə şəkillər</string>
|
||||
<string name="level">Səviyyə %d</string>
|
||||
<string name="profileLevel">%s (Səviyyə %s)</string>
|
||||
<string name="images_uploaded">Yüklənən şəkillər</string>
|
||||
<string name="image_reverts">Geri qaytarılan şəkillər</string>
|
||||
<string name="images_used_by_wiki">İstifadə olunan şəkillər</string>
|
||||
<string name="achievements_share_message">Nailiyyətlərinizi dostlarınızla paylaşın!</string>
|
||||
<string name="achievements_info_message">Bu tələbləri yerinə yetirdikcə səviyyəniz yüksəlir. \"Statistika\" bölməsindəki elementlər sizin səviyyənizi nəzərə almır.</string>
|
||||
<string name="achievements_revert_limit_message">minimum tələb olunur:</string>
|
||||
<string name="images_uploaded_explanation">İstənilən yükləmə proqramı vasitəsilə Vikianbara yüklədiyiniz şəkillərin sayı</string>
|
||||
<string name="images_reverted_explanation">Vikianbara yüklədiyiniz və silinməyən şəkillərin faizi</string>
|
||||
<string name="images_used_explanation">Vikianbara yüklədiyiniz və Vikimedia məqalələrində istifadə olunan şəkillərin sayı</string>
|
||||
<string name="error_occurred">Xəta baş verdi!</string>
|
||||
<string name="notifications_channel_name_all">Vikianbar bildirişi</string>
|
||||
<string name="preference_author_name_toggle">Fərdi müəllif adından istifadə et</string>
|
||||
<string name="preference_author_name_toggle_summary">Fotoşəkilləri yükləyərkən istifadəçi adınız əvəzinə fərdi müəllif adından istifadə edin</string>
|
||||
<string name="preference_author_name">Fərdi müəllif adı</string>
|
||||
<string name="contributions_fragment">Töhfələr</string>
|
||||
<string name="nearby_fragment">Yaxınlıqdakılar</string>
|
||||
<string name="notifications">Bildirişlər</string>
|
||||
<string name="read_notifications">Bildirişlər (oxunmuş)</string>
|
||||
<string name="display_nearby_notification">Yaxınlıqdakı bildirişini göstər</string>
|
||||
<string name="display_nearby_notification_summary">Şəkillərə ehtiyacı olan ən yaxın yerlər üçün tətbiqdaxili bildiriş göstərin</string>
|
||||
<string name="list_sheet">Siyahı</string>
|
||||
<string name="storage_permission">Yaddaş icazəsi</string>
|
||||
<string name="write_storage_permission_rationale_for_image_share">Şəkilləri yükləmək üçün cihazınızın xarici yaddaşına giriş icazəsi lazımdır.</string>
|
||||
<string name="nearby_notification_dismiss_message">Artıq şəkillərə ehtiyacı olan ən yaxın yeri görməyəcəksiniz. Bununla belə, istəyirsinizsə, bu bildirişi Parametrlərdə yenidən aktivləşdirə bilərsiniz.</string>
|
||||
<string name="send_thank_success_title">Təşəkkür uğurla göndərildi</string>
|
||||
<string name="send_thank_failure_message">Təşəkkür göndərilə bilmədi %1$s</string>
|
||||
<string name="send_thank_failure_title">Təşəkkür göndərilir: Xəta</string>
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Shvatio/la sam da je loša za moju privatnost</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Promenio/la sam mišljenje, ne želim da više javno budu vidljive</string>
|
||||
<string name="deletion_reason_not_interesting">Ova slika nije zanimljiva za enciklopediju</string>
|
||||
<string name="uploaded_by_myself">Otpremio/la sam na %1$s, koristi se u članku/cima — %2$d.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Otpremio/la sam na %1$s, koristi se u članku/cima — %2$d.</string>
|
||||
<string name="no_uploads">Dobro došli na Ostavu!\n\nOtpremite prve medije dodirom dugmeta za dodavanje.</string>
|
||||
<string name="no_categories_selected">Kategorije nisu izabrane</string>
|
||||
<string name="no_categories_selected_warning_desc">Slike bez kategorija retko su upotrebljive. Zaista želite da nastavite bez izbora kategorija?</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* Farorud
|
||||
* Ibrahim
|
||||
* ToJack
|
||||
* Vashgird
|
||||
|
|
@ -102,7 +103,7 @@
|
|||
<string name="navigation_item_logout">Баромад</string>
|
||||
<string name="navigation_item_notification">Огоҳиҳо</string>
|
||||
<string name="navigation_item_review">Тафтиш</string>
|
||||
<string name="nearby_info_menu_wikidata_article">Унсури Викидода</string>
|
||||
<string name="nearby_info_menu_wikidata_article">Элементи Викидода</string>
|
||||
<string name="navigation_item_login">Вуруд</string>
|
||||
<string name="nearby_directions">Ҷиҳат</string>
|
||||
<string name="nearby_wikidata">Викидода</string>
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
* Mohammed Galib Hasan
|
||||
* Muktogayn
|
||||
* NahidHossain
|
||||
* Nokib Sarkar
|
||||
* R4356th
|
||||
* Rasal Lia
|
||||
* Sankarshan
|
||||
|
|
@ -32,6 +33,7 @@
|
|||
<string name="commons_github">কমন্স গিটহাব উৎস কোড</string>
|
||||
<string name="commons_logo">কমন্সের লোগো</string>
|
||||
<string name="commons_website">কমন্স ওয়েবসাইট</string>
|
||||
<string name="exit_location_picker">লোকেশন পিকার থেকে বেরিয়ে আসুন</string>
|
||||
<string name="submit">জমা দিন</string>
|
||||
<string name="add_another_description">আরেকটি বিবরণ যোগ করুন</string>
|
||||
<string name="add_new_contribution">নতুন অবদান যোগ করুন</string>
|
||||
|
|
@ -44,6 +46,9 @@
|
|||
<string name="show_captions_description">বিবরণ</string>
|
||||
<string name="nearby_row_image">চিত্র</string>
|
||||
<string name="nearby_all">সব</string>
|
||||
<string name="nearby_filter_toggle">টগল আপ</string>
|
||||
<string name="nearby_filter_search">অনুসন্ধান দৃশ্য</string>
|
||||
<string name="nearby_filter_state">স্থানের অবস্থা</string>
|
||||
<string name="appwidget_img">আজকের চিত্র</string>
|
||||
<plurals name="uploads_pending_notification_indicator">
|
||||
<item quantity="one">%1$dটি ফাইল আপলোড হচ্ছে</item>
|
||||
|
|
@ -70,6 +75,10 @@
|
|||
<item quantity="one">%1$dটি আপলোড</item>
|
||||
<item quantity="other">%1$dটি আপলোড</item>
|
||||
</plurals>
|
||||
<plurals name="receiving_shared_content">
|
||||
<item quantity="one">শেয়ার করা কন্টেন্ট পাওয়া। ছবির আকার এবং আপনার ডিভাইসের উপর নির্ভর করে ছবিটি প্রক্রিয়াকরণে কিছুটা সময় লাগতে পারে</item>
|
||||
<item quantity="other">শেয়ার করা কন্টেন্ট পাওয়া। ছবির আকার এবং আপনার ডিভাইসের উপর নির্ভর করে ছবি প্রক্রিয়াকরণে কিছুটা সময় লাগতে পারে</item>
|
||||
</plurals>
|
||||
<string name="navigation_item_explore">অন্বেষণ</string>
|
||||
<string name="preference_category_appearance">অবয়ব</string>
|
||||
<string name="preference_category_general">সাধারণ</string>
|
||||
|
|
@ -89,9 +98,11 @@
|
|||
<string name="logging_in_message">অনুগ্রহ করে অপেক্ষা করুন…</string>
|
||||
<string name="updating_caption_title">ক্যাপশন ও বিবরণ হালনাগাদ করা হচ্ছে</string>
|
||||
<string name="updating_caption_message">অনুগ্রহ করে অপেক্ষা করুন…</string>
|
||||
<string name="login_success" fuzzy="true">প্রবেশ সফল!</string>
|
||||
<string name="login_success">প্রবেশ সফল</string>
|
||||
<string name="login_failed">প্রবেশ ব্যর্থ!</string>
|
||||
<string name="upload_failed">ফাইল পাওয়া যায়নি। আরেকটি ফাইল চেষ্টা করুন।</string>
|
||||
<string name="retry_limit_reached">সর্বোচ্চ পুনচেষ্টা করার সীমা পেরিয়ে গেছে! অনুগ্রহ করে আপলোডটি বাতিল করে আবার চেষ্টা করুন।</string>
|
||||
<string name="unrestricted_battery_mode">ব্যাটারি অপ্টিমাইজেশন বন্ধ করবেন?</string>
|
||||
<string name="authentication_failed">প্রমাণীকরণ ব্যর্থ হয়েছে। অনুগ্রহ করে আবার প্রবেশ করুন।</string>
|
||||
<string name="uploading_started">আপলোড আরম্ভ হয়েছে!</string>
|
||||
<string name="uploading_queued">আপলোড সারিবদ্ধ করা হয়েছে (সীমিত সংযোগ মোড সক্রিয় করা)</string>
|
||||
|
|
@ -113,6 +124,8 @@
|
|||
<string name="menu_from_camera">ছবি তুলুন</string>
|
||||
<string name="menu_nearby">কাছাকাছি</string>
|
||||
<string name="provider_contributions">আমার আপলোড</string>
|
||||
<string name="menu_copy_link">লিংক কপি করুন</string>
|
||||
<string name="menu_link_copied">লিঙ্কটি ক্লিপবোর্ডে কপি করা হয়েছে।</string>
|
||||
<string name="menu_share">বণ্টন</string>
|
||||
<string name="menu_view_file_page">ফাইলের পাতাটি দেখুন</string>
|
||||
<string name="share_title_hint">ক্যাপশন (আবশ্যক)</string>
|
||||
|
|
@ -123,7 +136,8 @@
|
|||
<string name="login_failed_throttled">খুব বেশি অসফল প্রচেষ্টা। অনুগ্রহ করে কয়েক মিনিট পরে আবারও চেষ্টা করুন।</string>
|
||||
<string name="login_failed_blocked">দুঃখিত, এই ব্যবহারকারীকে কমন্সে বাধা দেয়া হয়েছে</string>
|
||||
<string name="login_failed_2fa_needed">আপনাকে অবশ্যই আপনার দু\'স্তরের সত্যায়নকরণ কোড দিতে হবে।</string>
|
||||
<string name="login_failed_generic" fuzzy="true">প্রবেশ ব্যর্থ</string>
|
||||
<string name="login_failed_email_auth_needed">আপনার ইমেল ঠিকানায় একটি লগইন যাচাইকরণ কোড পাঠানো হয়েছে। লগ ইন করার জন্য অনুগ্রহ করে কোডটি প্রদান করুন।</string>
|
||||
<string name="login_failed_generic">প্রবেশ ব্যর্থ</string>
|
||||
<string name="share_upload_button">আপলোড</string>
|
||||
<string name="multiple_share_base_title">এই সেটের নাম</string>
|
||||
<string name="provider_modifications">পরিবর্তনসমূহ</string>
|
||||
|
|
@ -131,6 +145,7 @@
|
|||
<string name="categories_search_text_hint">বিষয়শ্রেণী অনুসন্ধান</string>
|
||||
<string name="depicts_search_text_hint">আপনার মিডিয়া চিত্রিত আইটেমগুলি অনুসন্ধান করুন (পর্বত, তাজমহল, ইত্যাদি)</string>
|
||||
<string name="menu_save_categories">সংরক্ষণ</string>
|
||||
<string name="menu_overflow_desc">ওভারফ্লো মেনু</string>
|
||||
<string name="refresh_button">পুনঃসতেজ</string>
|
||||
<string name="display_list_button">তালিকা</string>
|
||||
<string name="contributions_subtitle_zero">(আপলোড করেন নি)</string>
|
||||
|
|
@ -223,6 +238,7 @@
|
|||
<string name="become_a_tester_title">বিটা টেস্টার হোন</string>
|
||||
<string name="become_a_tester_description">গুগল প্লেতে আমাদের বেটা চ্যানেলে যুক্ত হন! এতে নতুন বৈশিষ্ট্যের পাশাপাশি পুরানো বাগগুলো\'র সংশোধিত রুপ সবার আগে ব্যবহারের সুযোগ পাবেন</string>
|
||||
<string name="_2fa_code">2FA কোড</string>
|
||||
<string name="email_auth_code">ইমেল যাচাইকরণ কোড</string>
|
||||
<string name="logout_verification">আপনি কি সত্যিই প্রস্থান করতে চান?</string>
|
||||
<string name="mediaimage_failed">মিডিয়া চিত্র ব্যর্থ হয়েছে</string>
|
||||
<string name="no_subcategory_found">কোন উপবিষয়শ্রেনী পাওয়া যায় নি।</string>
|
||||
|
|
@ -243,6 +259,7 @@
|
|||
<string name="navigation_item_about">সম্পর্কে</string>
|
||||
<string name="navigation_item_settings">সেটিং</string>
|
||||
<string name="navigation_item_feedback">প্রতিক্রিয়া</string>
|
||||
<string name="navigation_item_feedback_github">GitHub এর মাধ্যমে প্রতিক্রিয়া</string>
|
||||
<string name="navigation_item_logout">প্রস্থান</string>
|
||||
<string name="navigation_item_info">ভূমিকা</string>
|
||||
<string name="navigation_item_notification">বিজ্ঞপ্তি</string>
|
||||
|
|
@ -252,6 +269,7 @@
|
|||
<string name="nearby_info_menu_wikidata_article">উইকিউপাত্ত আইটেম</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">উইকিপিডিয়া নিবন্ধ</string>
|
||||
<string name="description_info">যতটা সম্ভব মিডিয়াটি বর্ণনা করুন: এটি কোথায় ধারণ করা হয়েছিল? এটি কি প্রদর্শন করে? এটির প্রসঙ্গ কি? ধারণকৃত বস্তু অথবা ব্যক্তির বর্ণনা করুন। সহজে অনুমান করা যায়না সেরকম তথ্য উদঘাটন করুন, উদাহরণস্বরূপ, যদি ল্যান্ডস্কেপ হয় তাহলে দিবসকালের সময় দিন।</string>
|
||||
<string name="caption_info">দয়া করে ছবির একটি সংক্ষিপ্ত বিবরণ লিখুন। প্রথম ক্যাপশনটি ছবির শিরোনাম হিসেবে ব্যবহার করা হবে। ২৫৫ অক্ষরের মধ্যে সীমাবদ্ধ।</string>
|
||||
<string name="upload_problem_exist">এই চিত্রের সম্ভাব্য সমস্যাগুলি:</string>
|
||||
<string name="upload_problem_image_dark">চিত্রটি খুব অন্ধকারযুক্ত।</string>
|
||||
<string name="upload_problem_image_blurry">চিত্রটি অস্পষ্ট।</string>
|
||||
|
|
@ -275,12 +293,13 @@
|
|||
<string name="nominated_see_more">বিশদের জন্য ওয়েবপৃষ্ঠা দেখুন</string>
|
||||
<string name="skip_login">এড়ান</string>
|
||||
<string name="navigation_item_login">প্রবেশ করুন</string>
|
||||
<string name="skip_login_title" fuzzy="true">আপনি কি সত্যিই প্রবেশকরণ এড়িয়ে যেতে চান?</string>
|
||||
<string name="skip_login_message" fuzzy="true">ভবিষ্যতে চিত্র আপলোড করতে চাইলে আপনাকে প্রবেশ করতে হবে।</string>
|
||||
<string name="skip_login_title">আপনি কি সত্যিই লগ-ইন এড়িয়ে যেতে চান?</string>
|
||||
<string name="skip_login_message">ভবিষ্যতে চিত্র আপলোড করতে চাইলে আপনাকে প্রবেশ করতে হবে।</string>
|
||||
<string name="login_alert_message">এই সুবিধাটি ব্যাবহার করতে প্রবেশ করুন</string>
|
||||
<string name="copy_wikicode">উইকিপাঠ্যটি ক্লিপবোর্ডে অনুলিপি করুন</string>
|
||||
<string name="wikicode_copied">উইকিপাঠ্যটি ক্লিপবোর্ডে অনুলিপি করা হয়েছে</string>
|
||||
<string name="nearby_location_not_available">কাছাকাছি সঠিকভাবে কাজ করছে না, অবস্থান উপলব্ধ নয়।</string>
|
||||
<string name="nearby_showing_pins_offline">ইন্টারনেট অনুপলব্ধ। শুধুমাত্র ক্যাশে করা স্থানগুলো দেখানো হচ্ছে।</string>
|
||||
<string name="location_permission_rationale_nearby">কাছাকাছি স্থানসমূহের একটি তালিকা প্রদর্শন করতে অনুমতি প্রয়োজন</string>
|
||||
<string name="nearby_directions">দিকনির্দেশ</string>
|
||||
<string name="nearby_wikidata">উইকিউপাত্ত</string>
|
||||
|
|
@ -334,19 +353,26 @@
|
|||
<string name="quiz_screenshot_question">এই স্ক্রিনশটটি কি আপলোড করা ঠিক হয়েছে?</string>
|
||||
<string name="share_app_title">অ্যাপ শেয়ার করুন</string>
|
||||
<string name="rotate">ঘোরান</string>
|
||||
<string name="error_fetching_nearby_places" fuzzy="true">কাছাকাছি স্থানগুলি আনতে ত্রুটি।</string>
|
||||
<string name="error_fetching_nearby_places">কাছাকাছি জায়গাগুলি লোড করা যায়নি</string>
|
||||
<string name="no_pictures_in_this_area">এই এলাকার কোনও ছবি নেই</string>
|
||||
<string name="no_nearby_places_around">আশেপাশে কোনো এলাকা পাওয়া যায়নি</string>
|
||||
<string name="error_fetching_nearby_monuments">আশেপাশের স্মৃতিস্তম্ভগুলি আনায়নে ত্রুটি৷</string>
|
||||
<string name="no_recent_searches">কোনও সাম্প্রতিক অনুসন্ধান নেই</string>
|
||||
<string name="delete_recent_searches_dialog">আপনি কি নিশ্চিত যে আপনি আপনার অনুসন্ধান ইতিহাস মুছে ফেলতে চান?</string>
|
||||
<string name="cancel_upload_dialog">আপনি কি নিশ্চিত যে আপনি এই আপলোডটি বাতিল করতে চান?</string>
|
||||
<string name="delete_search_dialog">আপনি কি এই অনুসন্ধানটি মুছে ফেলতে চান?</string>
|
||||
<string name="search_history_deleted">অনুসন্ধানের ইতিহাস মুছে ফেলা হয়েছে</string>
|
||||
<string name="nominate_delete">অপসারণের জন্য মনোনীত করুন</string>
|
||||
<string name="delete">অপসারণ</string>
|
||||
<string name="Achievements">কৃতিত্বগুলি</string>
|
||||
<string name="Profile">প্রোফাইল</string>
|
||||
<string name="badges">ব্যাজ</string>
|
||||
<string name="statistics">পরিসংখ্যান</string>
|
||||
<string name="statistics_thanks">ধন্যবাদ পেয়েছেন</string>
|
||||
<string name="statistics_featured">নির্বাচিত ছবি</string>
|
||||
<string name="level" fuzzy="true">স্তর</string>
|
||||
<string name="statistics_wikidata_edits">\"কাছাকাছি স্থান\" এর মাধ্যমে ছবি</string>
|
||||
<string name="level">স্তর %d</string>
|
||||
<string name="profileLevel">%s (স্তর %s )</string>
|
||||
<string name="images_uploaded">আপলোডকৃত চিত্র</string>
|
||||
<string name="image_reverts">ছবিগুলো প্রত্যাবর্তন করা হয়নি</string>
|
||||
<string name="images_used_by_wiki">ব্যবহৃত ছবি</string>
|
||||
|
|
@ -539,4 +565,5 @@
|
|||
<string name="failed">ব্যর্থ হয়েছে</string>
|
||||
<string name="caption">ক্যাপশন</string>
|
||||
<string name="caption_copied_to_clipboard">ক্যাপশন ক্লিপবোর্ডে অনুলিপি করা হয়েছে</string>
|
||||
<string name="nominated_for_deletion_btn">অপসারণের জন্য মনোনীত</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -448,7 +448,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Uvědomil/a jsem si, že je to špatné pro mé soukromí</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Změnil/a jsem názor, nechci, aby to bylo veřejně viditelné</string>
|
||||
<string name="deletion_reason_not_interesting">Omlouváme se, že tento obrázek není zajímavý pro encyklopedii</string>
|
||||
<string name="uploaded_by_myself">Náhráno mnou %1$s, použito v(e) %2$d článku/článcích.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Náhráno mnou %1$s, použito v(e) %2$d článku/článcích.</string>
|
||||
<string name="no_uploads">Vítejte na Commons!\n\nNahrajte svá první média klepnutím na tlačítko přidat.</string>
|
||||
<string name="no_categories_selected">Nebyly vybrány žádné kategorie</string>
|
||||
<string name="no_categories_selected_warning_desc">Obrázky bez kategorií jsou používány jen zřídka. Opravdu chcete nahrát obrázek bez výběru kategorií?</string>
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@
|
|||
<string name="categories_not_found">Ingen kategorier matchende %1$s er fundet</string>
|
||||
<string name="depictions_not_found">Der blev ikke fundet nogen Wikidata-elementer, der matcher %1$s</string>
|
||||
<string name="no_child_classes">%1$s har ingen underklasser</string>
|
||||
<string name="no_parent_classes">%1$s har ingen forældreklasser</string>
|
||||
<string name="no_parent_classes">%1$s har ingen overordnede klasser</string>
|
||||
<string name="categories_skip_explanation">Tilføj kategorier for at gøre dine billeder mere synlig på Wikimedia Commons. Begynd at taste for at tilføje kategorier.</string>
|
||||
<string name="categories_activity_title">Kategorier</string>
|
||||
<string name="title_activity_settings">Indstillinger</string>
|
||||
|
|
@ -419,7 +419,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Jeg indså, det var skidt for mit privatliv</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Jeg ombestemte mig, jeg ønsker ikke at det skal være offentligt synligt længere</string>
|
||||
<string name="deletion_reason_not_interesting">Beklager dette billede er ikke interessant for en encyklopædi</string>
|
||||
<string name="uploaded_by_myself">Uploadet af mig selv på %1$s , brugt i %2$d artikel(er).</string>
|
||||
<string name="uploaded_by_myself">Uploadet af mig selv på %1$s , brugt i mindst %2$d artikel(er).</string>
|
||||
<string name="no_uploads">Velkommen til Commons!\n\nUpload dit første medie ved at trykke på Tilføj-knappen.</string>
|
||||
<string name="no_categories_selected">Ingen kategorier valgt</string>
|
||||
<string name="no_categories_selected_warning_desc">Billeder uden kategorier er sjældent brugbare. Er du sikker på, at du vil fortsætte uden at vælge kategorier?</string>
|
||||
|
|
@ -584,6 +584,8 @@
|
|||
<string name="title_for_media">MEDIER</string>
|
||||
<string name="title_for_child_classes">UNDERKLASSER</string>
|
||||
<string name="title_for_parent_classes">OVERORDNEDE KLASSER</string>
|
||||
<string name="title_for_subcategories">UNDERKATEGORIER</string>
|
||||
<string name="title_for_parent_categories">OVERORDNEDE KATEGORIER</string>
|
||||
<string name="upload_nearby_place_found_title">Sted fundet i nærheden</string>
|
||||
<string name="upload_nearby_place_found_description_plural">Er disse billeder af %1$s ?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">Er dette et billede af %1$s?</string>
|
||||
|
|
@ -828,4 +830,5 @@
|
|||
<string name="show_in_nearby">Vis i I nærheden</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Oprettet og uploadet af: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Oprettet af %1$s og uploadet af %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Nomineret til sletning</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -453,7 +453,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Ich habe erkannt, dass es schlecht für meine Privatsphäre ist.</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Ich habe meine Meinung geändert. Ich möchte nicht mehr, dass es öffentlich sichtbar ist.</string>
|
||||
<string name="deletion_reason_not_interesting">Entschuldigung, aber dieses Bild ist für eine Enzyklopädie nicht relevant.</string>
|
||||
<string name="uploaded_by_myself">Von mir selbst hochgeladen am %1$s, verwendet in %2$d Artikel(n).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Von mir selbst hochgeladen am %1$s, verwendet in %2$d Artikel(n).</string>
|
||||
<string name="no_uploads">Willkommen bei Commons!\n\nLade deine erste Datei hoch, indem du auf die Hinzufügen-Schaltfläche tippst.</string>
|
||||
<string name="no_categories_selected">Keine Kategorien ausgewählt</string>
|
||||
<string name="no_categories_selected_warning_desc">Bilder ohne Kategorien sind selten nutzbar. Bist du sicher, dass du ohne die Auswahl von Kategorien hochladen möchtest?</string>
|
||||
|
|
|
|||
|
|
@ -433,7 +433,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Συνειδητοποίησα ότι είναι κακό για την ιδιωτικότητά μου</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Άλλαξα γνώμη, δε θέλω να προβάλλεται πλέον δημόσια</string>
|
||||
<string name="deletion_reason_not_interesting">Συγγνώμη, αυτή η φωτογραφία δεν είναι ενδιαφέρουσα για μια εγκυκλοπαίδεια</string>
|
||||
<string name="uploaded_by_myself">Ανέβηκε από εμένα στο %1$s, χρησιμοποιήθηκε σε %2$d άρθρο/α</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Ανέβηκε από εμένα στο %1$s, χρησιμοποιήθηκε σε %2$d άρθρο/α</string>
|
||||
<string name="no_uploads">Καλώς ήρθατε στα Commons!\n\nΑνεβάστε τα πρώτα σας πολυμέσα πατώντας το κουμπί της προσθήκης.</string>
|
||||
<string name="no_categories_selected">Δεν επιλέχθηκαν κατηγορίες</string>
|
||||
<string name="no_categories_selected_warning_desc">Οι εικόνες χωρίς κατηγορίες χρησιμοποιούνται σπάνια. Θέλετε πράγματι να συνεχίσετε δίχως να επιλέξετε κατηγορίες;</string>
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Mi eksciis ke ĝi esta malbona por mia privateco</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Mi ŝanĝis mian preferon; mi ne plu volas ke ĝi estu publike videbla</string>
|
||||
<string name="deletion_reason_not_interesting">Domaĝe, ĉi tiu bildo ne estas interesa por enciklopedio</string>
|
||||
<string name="uploaded_by_myself">Alŝutita de mi je %1$s, uzata en %2$d artikolo(j).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Alŝutita de mi je %1$s, uzata en %2$d artikolo(j).</string>
|
||||
<string name="no_uploads">Bonvenon al Komunejo!\n\nAlŝutu vian unuan aŭdvidaĵon per frapeto de la butono \'Aldoni\'.</string>
|
||||
<string name="no_categories_selected">Neniu Elektita Kategorio</string>
|
||||
<string name="no_categories_selected_warning_desc">Bildoj sen kategorioj estas malofte uzebla. Ĉu vi certas, ke vi volas daŭrigi sen elekti kategoriojn?</string>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
* Jduranboger
|
||||
* Jelou
|
||||
* Johnny243
|
||||
* JorgeElias2302
|
||||
* Josuert
|
||||
* Juanman
|
||||
* Keneth Urrutia
|
||||
|
|
@ -152,6 +153,7 @@
|
|||
<string name="menu_nearby">Cercanos</string>
|
||||
<string name="provider_contributions">Mis subidas</string>
|
||||
<string name="menu_copy_link">Copiar enlace</string>
|
||||
<string name="menu_link_copied">El enlace ha sido copiado al portapapeles.</string>
|
||||
<string name="menu_share">Compartir</string>
|
||||
<string name="menu_view_file_page">Ver página del archivo</string>
|
||||
<string name="share_title_hint">Leyenda (requerido)</string>
|
||||
|
|
@ -162,6 +164,7 @@
|
|||
<string name="login_failed_throttled">Demasiados intentos fallidos. Inténtalo de nuevo en unos minutos.</string>
|
||||
<string name="login_failed_blocked">Lo sentimos, este usuario ha sido bloqueado en Commons</string>
|
||||
<string name="login_failed_2fa_needed">Debe proporcionar su código de autenticación de dos factores.</string>
|
||||
<string name="login_failed_email_auth_needed">Se ha enviado un código de verificación de inicio de sesión a tu correo electrónico. Indícalo para iniciar sesión.</string>
|
||||
<string name="login_failed_generic">Falló el inicio de sesión</string>
|
||||
<string name="share_upload_button">Subir</string>
|
||||
<string name="multiple_share_base_title">Nombrar este conjunto</string>
|
||||
|
|
@ -267,6 +270,7 @@
|
|||
<string name="become_a_tester_title">Prueba la versión beta</string>
|
||||
<string name="become_a_tester_description">Apúntate a nuestro canal beta en Google Play y obtén acceso a funcionalidades nuevas y correcciones de errores</string>
|
||||
<string name="_2fa_code">Código de autenticación de 2 pasos</string>
|
||||
<string name="email_auth_code">Código de verificación de correo electrónico</string>
|
||||
<string name="logout_verification">¿Confirmas que quieres salir?</string>
|
||||
<string name="mediaimage_failed">Falló la imagen de multimedia</string>
|
||||
<string name="no_subcategory_found">No se encontró ninguna subcategoría</string>
|
||||
|
|
@ -327,6 +331,7 @@
|
|||
<string name="copy_wikicode">Copia el wikitexto al portapapeles</string>
|
||||
<string name="wikicode_copied">El wikitexto fue copiado al portapapeles</string>
|
||||
<string name="nearby_location_not_available">Cercanos no puede funcionar correctamente. La ubicación no está disponible.</string>
|
||||
<string name="nearby_showing_pins_offline">Internet no disponible. Solo se muestran ubicaciones en caché.</string>
|
||||
<string name="upload_location_access_denied">Acceso a la ubicación denegado. Configura tu ubicación manualmente para utilizar esta función.</string>
|
||||
<string name="location_permission_rationale_nearby">Se necesita permiso para mostrar una lista de lugares cercanos</string>
|
||||
<string name="location_permission_rationale_explore">Se necesita permiso para mostrar una lista de lugares cercanos</string>
|
||||
|
|
@ -416,6 +421,7 @@
|
|||
<string name="statistics_featured">Imágenes destacadas</string>
|
||||
<string name="statistics_wikidata_edits">Imágenes vía \"Sitios Cercanos\"</string>
|
||||
<string name="level">Nivel %d</string>
|
||||
<string name="profileLevel">%s (Nivel %s)</string>
|
||||
<string name="images_uploaded">Imágenes subidas</string>
|
||||
<string name="image_reverts">Imágenes no revertidas</string>
|
||||
<string name="images_used_by_wiki">Imágenes utilizadas</string>
|
||||
|
|
@ -447,6 +453,7 @@
|
|||
<string name="map_application_missing">No se encontró una aplicación de mapas compatible en tu dispositivo. Instala una para usar esta característica.</string>
|
||||
<string name="title_page_bookmarks_pictures">Imágenes</string>
|
||||
<string name="title_page_bookmarks_locations">Ubicaciones</string>
|
||||
<string name="title_page_bookmarks_categories">Categorías</string>
|
||||
<string name="menu_bookmark">Añadir o quitar de marcadores</string>
|
||||
<string name="provider_bookmarks">Marcadores</string>
|
||||
<string name="bookmark_empty">No has añadido ningún marcador</string>
|
||||
|
|
@ -457,7 +464,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Me di cuenta que es malo para mi privacidad</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Cambié de opinión, no quiero que siga siendo visible públicamente</string>
|
||||
<string name="deletion_reason_not_interesting">Lo sentimos, esta imagen no es interesante para una enciclopedia</string>
|
||||
<string name="uploaded_by_myself">Subida por mí mismo el %1$s, usado en %2$d artículo(s).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Subida por mí mismo el %1$s, usado en %2$d artículo(s).</string>
|
||||
<string name="no_uploads">Te damos la bienvenida a Commons.\n\nCarga tu primer archivo mediante el botón Añadir.</string>
|
||||
<string name="no_categories_selected">No hay categorías seleccionadas</string>
|
||||
<string name="no_categories_selected_warning_desc">Las imágenes sin categorías raramente se pueden usar. ¿Seguro que quieres continuar sin seleccionar ninguna categoría?</string>
|
||||
|
|
@ -827,6 +834,7 @@
|
|||
<string name="talk">Discusión</string>
|
||||
<string name="write_something_about_the_item">Escriba algo sobre el elemento \'%1$s\'. Será visible públicamente.</string>
|
||||
<string name="does_not_exist_anymore_no_picture_can_ever_be_taken_of_it">\' %1$s \' ya no existe, nunca se podrá tomar ninguna fotografía de él.</string>
|
||||
<string name="is_at_a_different_place_wikidata">\'%1$s\' está en un lugar diferente.</string>
|
||||
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\' %1$s \' está en un lugar diferente. Especifique el lugar correcto a continuación y, si es posible, escriba la latitud y longitud correctas.</string>
|
||||
<string name="other_problem_or_information_please_explain_below">Otro problema o información (por favor explique a continuación).</string>
|
||||
<string name="feedback_destination_note">Sus comentarios se publicarán en la siguiente página wiki: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile_app/Feedback</a></string>
|
||||
|
|
@ -848,4 +856,21 @@
|
|||
<string name="red_pin">Este lugar aún no tiene foto, ¡ve y toma una!</string>
|
||||
<string name="green_pin">Este lugar ya tiene una foto.</string>
|
||||
<string name="grey_pin">Ahora comprobando si este lugar tiene una foto.</string>
|
||||
<string name="error_while_loading">Error al cargar</string>
|
||||
<string name="no_usages_found">No se encontraron usos</string>
|
||||
<string name="usages_on_commons_heading">Commons</string>
|
||||
<string name="usages_on_other_wikis_heading">Otras wikis</string>
|
||||
<string name="file_usages_container_heading">Usos de archivos</string>
|
||||
<string name="title_activity_single_web_view">Actividad de vista web única</string>
|
||||
<string name="account">Cuenta</string>
|
||||
<string name="vanish_account">Desaparecer cuenta</string>
|
||||
<string name="account_vanish_request_confirm_title">Advertencia sobre la desaparición de la cuenta</string>
|
||||
<string name="account_vanish_request_confirm">La desaparición de una cuenta en Wikimedia Commons es un <b>último recurso</b> y <b>solo debe usarse cuando desee dejar de editar para siempre y también para ocultar la mayor cantidad posible de sus asociaciones pasadas.<br/><br/>La desaparición de una cuenta en Wikimedia Commons se realiza cambiando el nombre de la cuenta para que otros no puedan reconocer sus contribuciones en un proceso llamado desaparición de la cuenta. <b>La desaparición de la cuenta no garantiza el anonimato completo ni elimina las contribuciones a los proyectos</b>.</string>
|
||||
<string name="caption">Leyenda</string>
|
||||
<string name="caption_copied_to_clipboard">Leyenda copiado al portapapeles</string>
|
||||
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Felicitaciones, todas las imágenes de este álbum han sido cargadas o marcadas como no para cargar.</string>
|
||||
<string name="show_in_explore">Mostrar en Explorar</string>
|
||||
<string name="show_in_nearby">Mostrar en las cercanías</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Creado y cargado por: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Creado por %1$s y cargado por %2$s</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -386,7 +386,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">فهمیدم که این برای حریم خصوصی من بد است.</string>
|
||||
<string name="deletion_reason_no_longer_want_public">من تغییر عقیده دادم، نمیخواهم دیگر برای همه قابلمشاهده باشد.</string>
|
||||
<string name="deletion_reason_not_interesting">با پوزش، این تصویر برای یک دانشنامه مناسب نیست</string>
|
||||
<string name="uploaded_by_myself">بارگذاریشده توسط خودم در %1$s؛ استفادهشده در %2$d مقاله.</string>
|
||||
<string name="uploaded_by_myself">بارگذاریشده توسط خودم در %1$s؛ استفادهشده در دست کم %2$d مقاله.</string>
|
||||
<string name="no_uploads">به ویکیانبار خوش آمدید!\n\nاولین فایلتان را با فشردن کلید اضافه بارگذاری کنید.</string>
|
||||
<string name="no_categories_selected">ردهای انتخاب نشده است</string>
|
||||
<string name="no_categories_selected_warning_desc">تصاویر بدون رده به ندرت قابلاستفاده هستند. آیا مطمئنید که میخواهید بدون انتخاب رده ادامه دهید؟</string>
|
||||
|
|
@ -508,4 +508,5 @@
|
|||
<string name="your_feedback">بازخورد شما</string>
|
||||
<string name="see_your_achievements">دستاوردهای خود را ببینید</string>
|
||||
<string name="could_not_load_place_data">ناتوان از بارگیری دادههای مکانی</string>
|
||||
<string name="nominated_for_deletion_btn">نامزدشده برای حذف</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@
|
|||
<string name="tutorial_4_subtext_2">Kuvaus: Sydneyn oopperatalo lahden toiselta puolelta katsottuna</string>
|
||||
<string name="tutorial_4_subtext_3">Luokat: Sydneyn oopperatalo lännestä katsottuna, Sydneyn oopperatalo kaukaa katsottuna</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_wikipedia_subtext">Wikipedian kuvat ovat peräisin Wikimedia Commonsista.</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>
|
||||
|
|
@ -404,7 +404,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Huomasin, että se on haitallinen yksityisyydelleni.</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Muutin mieleni, en halua että se on enää julkisesti näkyvissä.</string>
|
||||
<string name="deletion_reason_not_interesting">Valitettavasti tämä kuva ei ole kiinnostava tietosanakirjaan</string>
|
||||
<string name="uploaded_by_myself">Minä olen ladannut kohteen %1$s käytetty %2$d artikkelissa.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Minä olen ladannut kohteen %1$s käytetty %2$d artikkelissa.</string>
|
||||
<string name="no_uploads">Tervetuloa Commonsiin!\n\nTallenna ensimmäinen mediasi koskettamalla lisäyspainiketta.</string>
|
||||
<string name="no_categories_selected">Luokkia ei valittu</string>
|
||||
<string name="no_categories_selected_warning_desc">Kuvat, jotka eivät ole luokissa, ovat harvoin käyttökelpoisia. Haluatko varmasti jatkaa valitsematta luokkia?</string>
|
||||
|
|
|
|||
|
|
@ -457,7 +457,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">J’ai pensé que ce n’était pas bon pour ma vie privée</string>
|
||||
<string name="deletion_reason_no_longer_want_public">J’ai changé d\'avis, je ne désire plus que cel soit visible publiquement</string>
|
||||
<string name="deletion_reason_not_interesting">Désolé mais cette image n’est pas intéressante pour une encyclopédie</string>
|
||||
<string name="uploaded_by_myself">Téléversé par moi-même le %1$s, utilisé dans %2$d article(s).</string>
|
||||
<string name="uploaded_by_myself">Téléversé par moi-même le %1$s, utilisé dans au moins %2$d article(s).</string>
|
||||
<string name="no_uploads">Bienvenue sur Commons !\n\nTéléversez votre premier média en tapant sur le bouton Ajouter.</string>
|
||||
<string name="no_categories_selected">Aucune catégorie sélectionnée</string>
|
||||
<string name="no_categories_selected_warning_desc">Les images sans catégories sont rarement utilisables. Voulez-vous vraiment continuer sans sélectionner des catégories appropriées ?</string>
|
||||
|
|
@ -869,4 +869,5 @@
|
|||
<string name="show_in_nearby">Afficher à proximité</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Crée et publiée par: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Créé par %1$s et publiée par %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Nommé pour suppression</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -394,7 +394,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Decateime de que prexudica a miña privacidade</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Cambiei de idea, non quero que siga sendo visible de forma pública</string>
|
||||
<string name="deletion_reason_not_interesting">Desculpas, esta imaxe non é interesante para unha enciclopedia</string>
|
||||
<string name="uploaded_by_myself">Cargada por min o %1$s, usada en %2$d artigo(s).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Cargada por min o %1$s, usada en %2$d artigo(s).</string>
|
||||
<string name="no_uploads">Dámoslle a benvida ó Commonsǃ\n\nCargue o seu primeiro ficheiro premendo no botón Engadir.</string>
|
||||
<string name="no_categories_selected">Non hai categorías seleccionadas</string>
|
||||
<string name="no_categories_selected_warning_desc">As imaxes sen categorías só son utilizables en contadas ocasións. Está seguro de que quere continuar sen seleccionar categorías?</string>
|
||||
|
|
|
|||
|
|
@ -362,7 +362,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Észleltem, hogy ez rossz a magánszférámnak</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Meggondoltam magam, nem szeretném, hogy nyilvánosan látható legyen</string>
|
||||
<string name="deletion_reason_not_interesting">Sajnos ez a fénykép nem érdekes egy enciklopédia számára</string>
|
||||
<string name="uploaded_by_myself">Feltöltve ekkorː %1$s, %2$d cikkben használva.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Feltöltve ekkorː %1$s, %2$d cikkben használva.</string>
|
||||
<string name="no_uploads">Üdvözlünk a Commons-ban. \n\nA hozzáadás gombra koppintva feltöltheted első képedet.</string>
|
||||
<string name="no_categories_selected">Nincs kiválasztott kategória</string>
|
||||
<string name="no_categories_selected_warning_desc">A kategória nélküli képek ritkán használhatóak. Biztos, hogy kategória kiválasztása nélkül akarsz továbblépni?</string>
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Io ha comprendite que es mal pro mi vita private</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Io ha cambiate de idea, io non vole plus que illo sia publicamente visibile</string>
|
||||
<string name="deletion_reason_not_interesting">Iste imagine non es interessante pro un encyclopedia</string>
|
||||
<string name="uploaded_by_myself">Incargate per me mesme le %1$s, usate in %2$d articulo(s).</string>
|
||||
<string name="uploaded_by_myself">Incargate per me mesme le %1$s, usate al minus in %2$d articulo(s).</string>
|
||||
<string name="no_uploads">Benvenite a Commons!\n\nPro incargar tu prime file multimedial, tocca le button Adder.</string>
|
||||
<string name="no_categories_selected">Necun categoria seligite</string>
|
||||
<string name="no_categories_selected_warning_desc">Imagines sin categorias es rarmente usabile. Es tu secur de voler continuar sin seliger categorias?</string>
|
||||
|
|
@ -816,4 +816,5 @@
|
|||
<string name="show_in_nearby">Monstrar in A proximitate</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Create e incargate per: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Create per %1$s e incargate per %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Nominate pro deletion</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Saya menyadari itu buruk untuk privasi saya</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Saya berubah pikiran, saya tidak ingin itu terlihat publik lagi</string>
|
||||
<string name="deletion_reason_not_interesting">Maaf gambar ini tidak menarik untuk ensiklopedia</string>
|
||||
<string name="uploaded_by_myself">Diunggah saya sendiri pada %1$s, digunakan dalam %2$d artikel.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Diunggah saya sendiri pada %1$s, digunakan dalam %2$d artikel.</string>
|
||||
<string name="no_uploads">Unggah media pertama Anda dengan mengetuk tombol.</string>
|
||||
<string name="no_categories_selected">Tidak ada Kategori yang Dipilih</string>
|
||||
<string name="no_categories_selected_warning_desc">Gambar tanpa kategori jarang dapat digunakan. Apakah Anda yakin ingin mengirim tanpa memilih kategori?</string>
|
||||
|
|
|
|||
|
|
@ -410,7 +410,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Me konstatis ke ol esas mala por mea privateso</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Me chanjis mea ideo: me ne pluse deziras ke ol esos publike videbla</string>
|
||||
<string name="deletion_reason_not_interesting">Pardonez! Ca imajo ne esas interesanta por ula enciklopedio</string>
|
||||
<string name="uploaded_by_myself">Adjuntita da me, che %1$s, uzita en %2$d artiklo/artikli.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Adjuntita da me, che %1$s, uzita en %2$d artiklo/artikli.</string>
|
||||
<string name="no_uploads">Bonveno a Commons!\n\nSendez vua unesma arkivo kliktanta sur butono \"adjuntez\" (\'\'add\'\').</string>
|
||||
<string name="no_categories_selected">Nula kategorio selektita</string>
|
||||
<string name="no_categories_selected_warning_desc">Imaji sen kategorii rare esas uzebla. Ka vu fakte deziras sendar ol sen selektar irga kategorio?</string>
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Ég áttaði mig á að þetta væri slæmt fyrir gagnaleynd mína</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Ég skipti um skoðun, ég vil ekki að hún sé lengur öllum sýnileg</string>
|
||||
<string name="deletion_reason_not_interesting">Afsakið, þessi mynd hefur ekkert gildi fyrir alfræðirit</string>
|
||||
<string name="uploaded_by_myself">Sent inn af mér sjálfum %1$s, notað í %2$d grein(um).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Sent inn af mér sjálfum %1$s, notað í %2$d grein(um).</string>
|
||||
<string name="no_uploads">Velkomin í Commons!\n\nSendu inn fyrstu margmiðlunargögnin þín með því að ýta á viðbætingarhnappinn.</string>
|
||||
<string name="no_categories_selected">Engir flokkar valdir</string>
|
||||
<string name="no_categories_selected_warning_desc">Myndir án flokka eru sjaldnast nýtilegar. Ertu viss um að þú viljir halda áfram án þess að velja flokka?</string>
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@
|
|||
<string name="categories_search_text_hint">Cerca categorie</string>
|
||||
<string name="depicts_search_text_hint">Cerca elementi che il tuo file rappresenta (montagna, Taj Mahal, ecc.)</string>
|
||||
<string name="menu_save_categories">Salva</string>
|
||||
<string name="menu_overflow_desc">Menu overflow</string>
|
||||
<string name="refresh_button">Aggiorna</string>
|
||||
<string name="display_list_button">Elenco</string>
|
||||
<string name="contributions_subtitle_zero">(Non è stato ancora caricato niente)</string>
|
||||
|
|
@ -289,7 +290,7 @@
|
|||
<string name="send_log_file_description">Invia il file di registro agli sviluppatori tramite email per aiutarli a risolvere i problemi con l\'applicazione. Nota: i log potrebbero contenere informazioni di identificazione</string>
|
||||
<string name="no_web_browser">Nessun browser web trovato per aprire l\'URL</string>
|
||||
<string name="null_url">Errore! URL non trovato</string>
|
||||
<string name="nominate_deletion">Proponi per la Cancellazione</string>
|
||||
<string name="nominate_deletion">Proponi per la cancellazione</string>
|
||||
<string name="nominated_for_deletion">Questa immagine è stata proposta per la cancellazione.</string>
|
||||
<string name="nominated_see_more">Vedi la pagina web per i dettagli</string>
|
||||
<string name="skip_login">Salta</string>
|
||||
|
|
@ -433,7 +434,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Mi sono reso conto che viola la mia privacy</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Ho cambiato idea, non voglio più che sia pubblicamente visibile</string>
|
||||
<string name="deletion_reason_not_interesting">Spiacente, questa immagine non è interessante per un\'enciclopedia</string>
|
||||
<string name="uploaded_by_myself">Caricato da me stesso il %1$s, utilizzato in %2$d voce/i.</string>
|
||||
<string name="uploaded_by_myself">Caricato da me stesso il %1$s, utilizzato almeno in %2$d voce/i.</string>
|
||||
<string name="no_uploads">Benvenuto su Commons!\n\nCarica il tuo primo file multimediale toccando il pulsante aggiungi.</string>
|
||||
<string name="no_categories_selected">Nessuna categoria selezionata</string>
|
||||
<string name="no_categories_selected_warning_desc">Le immagini senza categorie sono raramente utilizzabili. Sei sicuro di voler continuare senza selezionare le categorie?</string>
|
||||
|
|
@ -594,6 +595,7 @@
|
|||
<string name="title_for_media">MEDIA</string>
|
||||
<string name="title_for_child_classes">CLASSI FIGLIE</string>
|
||||
<string name="title_for_parent_classes">CLASSI SUPERIORI</string>
|
||||
<string name="title_for_subcategories">Sottocategorie</string>
|
||||
<string name="upload_nearby_place_found_title">Rinvenuto luogo nei pressi</string>
|
||||
<string name="upload_nearby_place_found_description_plural">Queste sono immagini di %1$s?</string>
|
||||
<string name="upload_nearby_place_found_description_singular">Questa è un\'immagine di %1$s?</string>
|
||||
|
|
@ -771,7 +773,7 @@
|
|||
<string name="learn_how_to_write_a_useful_caption">Impara come scrivere una didascalia utile</string>
|
||||
<string name="see_your_achievements">Vedi i tuoi risultati</string>
|
||||
<string name="edit_image">Modifica Immagine</string>
|
||||
<string name="edit_location">Modifica Posizione</string>
|
||||
<string name="edit_location">Modifica posizione</string>
|
||||
<string name="location_updated">Posizione aggiornata!</string>
|
||||
<string name="remove_location">Rimuovi posizione</string>
|
||||
<string name="remove_location_warning_title">Rimuovi avviso di posizione</string>
|
||||
|
|
@ -836,4 +838,5 @@
|
|||
<string name="show_in_nearby">Mostra nelle vicinanze</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Creato e caricato da: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Creato da %1$s e caricato da %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Proposto per la cancellazione</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">הבנתי שזה לא טוב לפרטיות שלי</string>
|
||||
<string name="deletion_reason_no_longer_want_public">התחרטתי, אינני רוצה עוד שהיא תוצג לציבור</string>
|
||||
<string name="deletion_reason_not_interesting">סליחה, התמונה אינה מעניינת עבור אנציקלופדיה</string>
|
||||
<string name="uploaded_by_myself">הועלה על ידי ב-%1$s, בשימוש ב-%2$d מאמר(ים)</string>
|
||||
<string name="uploaded_by_myself">הועלה על ידי ב־%1$s, בשימוש ב־%2$d ערכים לפחות.</string>
|
||||
<string name="no_uploads">ברוך בואך לוויקישיתוף!\n\nכדי להעלות את המדיה הראשונה שלך, נא להקיש על כפתור ההוספה.</string>
|
||||
<string name="no_categories_selected">לא נבחרו קטגוריות</string>
|
||||
<string name="no_categories_selected_warning_desc">תמונות ללא קטגוריות בדרך כלל אינן שימושיות. להמשיך ללא בחירת קטגוריות?</string>
|
||||
|
|
@ -862,4 +862,5 @@
|
|||
<string name="show_in_nearby">בתצוגת בסביבה</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">נוצר והועלה על־ידי: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">נוצר על־ידי %1$s והועלה על־ידי %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">הועמדה למחיקה</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">自分のプライバシーを傷つけると気がつきました</string>
|
||||
<string name="deletion_reason_no_longer_want_public">以前とは考えが変わりました。今後は公衆の場で自分の画像を公開したくありません</string>
|
||||
<string name="deletion_reason_not_interesting">残念ですがこの画像は百科事典の目的に合いません</string>
|
||||
<string name="uploaded_by_myself">私本人が%1$sにアップロードし、%2$d件の記事で使用されました。</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">私本人が%1$sにアップロードし、%2$d件の記事で使用されました。</string>
|
||||
<string name="no_uploads">コモンズへようこそ!\n\n追加ボタンを押して、ぜひご自分のメディアの初投稿をしましょう。</string>
|
||||
<string name="no_categories_selected">カテゴリが選択されていません</string>
|
||||
<string name="no_categories_selected_warning_desc">カテゴリを指定しない画像は使用されることがほとんどありません。ほんとうにカテゴリを選択しないまま作業を続けますか?</string>
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@
|
|||
<string name="login_failed_throttled">Кёб джетишимсиз сынау болду. Тилейбиз, талай минст сора энтда кёрюгюз.</string>
|
||||
<string name="login_failed_blocked">Кечериксиз, бу хайырланыучу Гёзенде блок этилгенди</string>
|
||||
<string name="login_failed_2fa_needed">Эки факторлу аутентификация кодну берирге керексиз.</string>
|
||||
<string name="login_failed_email_auth_needed">Бегитиу код электрон почта адресигизге джиберилгенди. Кирир ючюн аны салыгъыз.</string>
|
||||
<string name="login_failed_generic">Системагъа кириуде халат</string>
|
||||
<string name="share_upload_button">Джюкле</string>
|
||||
<string name="multiple_share_base_title">Бу къауумха ат бер</string>
|
||||
|
|
@ -216,6 +217,7 @@
|
|||
<string name="become_a_tester_title">Бета-Тестер болугъуз</string>
|
||||
<string name="become_a_tester_description">Google Play-де бета каналыбызгъа къошулугъуз эмда джангы функциялагъа эмда халат тюзетиулеге эртде джетишигиз</string>
|
||||
<string name="_2fa_code">2FA Код</string>
|
||||
<string name="email_auth_code">Электрон почтаны верификация этигиз</string>
|
||||
<string name="logout_verification">Кертиден чыгъаргъа излеймисиз?</string>
|
||||
<string name="mediaimage_failed">Медиа Сурат Джетишимсизди</string>
|
||||
<string name="no_subcategory_found">Тюб категория табылмады</string>
|
||||
|
|
@ -409,7 +411,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Ташалыгъыма аман болгъанын ангыладым</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Сагъышымы тюрлендирдим, энди хар кимге кёрюннюгюмю излемейме</string>
|
||||
<string name="deletion_reason_not_interesting">Кечериксиз, бу сурат энциклопедия ючюн эс бёлюрча тюлдю</string>
|
||||
<string name="uploaded_by_myself">Кесим джюклеген %1$s сайтха джюкленди эмда %2$d статьяда хайырландырылды.</string>
|
||||
<string name="uploaded_by_myself">Кесим джюклеген %1$s сайтха джюкленди эмда эм азы бла %2$d статьяда хайырландырылды.</string>
|
||||
<string name="no_uploads">Гёзеннге Хош Келигиз!\n\nКъош тиекге басыб биринчи медиагъызны джюклегиз.</string>
|
||||
<string name="no_categories_selected">Категорияла Сайланмадыла</string>
|
||||
<string name="no_categories_selected_warning_desc">Категориясыз суратла аз хайырланадыла. Категория сайламай бардырыргъа излегенигизге ишексизмисиз?</string>
|
||||
|
|
@ -779,6 +781,7 @@
|
|||
<string name="talk">Сюзюу</string>
|
||||
<string name="write_something_about_the_item">%1$s элементни юсюнден бир затла джазыгъыз. Буну хар ким да кёрлюкдю.</string>
|
||||
<string name="does_not_exist_anymore_no_picture_can_ever_be_taken_of_it">\'%1$s\' энди бар тюлдю, аны картха алырча тюлдю.</string>
|
||||
<string name="is_at_a_different_place_wikidata">\'%1$s\' башха джердеди.</string>
|
||||
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\'%1$s\' - башха джерди. Тилейбиз, тюз джерни энишгерекде белгилегиз, эмда мадар бар эсе, тюз кенглик бла узунлукъну джазыгъыз.</string>
|
||||
<string name="other_problem_or_information_please_explain_below">Башха проблема неда информация (тилейбиз, энишгерекде ангылатыгъыз).</string>
|
||||
<string name="feedback_destination_note">Сизни кери оюмугъуз тюбюндеги вики бетге джиберилликди: <a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\">Commons:Mobile app/Feedback</a></string>
|
||||
|
|
@ -815,4 +818,7 @@
|
|||
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Алгъышлайбыз, бу альбомна бютеу сратла не джюкленнгендиле, неда джюкленирге джораланмагъанлача белгиленнгендиле.</string>
|
||||
<string name="show_in_explore">Explore-де кёргюз</string>
|
||||
<string name="show_in_nearby">Nearby-да кёргюз</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Болдургъан эм джюклеген: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">%1$s болдурду эмда %2$s джюкледи</string>
|
||||
<string name="nominated_for_deletion_btn">Кетериуге теджелгенди</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -345,7 +345,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">M tɛn\'ɛs ye di na an bɛ\'ɛd tisi m mɛŋ gu\'udim yɛla</string>
|
||||
<string name="deletion_reason_no_longer_want_public">M tiaki m pʋtɛ\'ɛr, m pʋ lɛm bɔɔd ye di bɛ zin\'ikanɛ ka sɔ\' wʋsa na nyɛ ya\'asa</string>
|
||||
<string name="deletion_reason_not_interesting">Gafara footo kaŋa pʋ nar ye di bɛ encyclopedia</string>
|
||||
<string name="uploaded_by_myself">M kpɛ\'ɛsidinɛ %1$s m mɛŋ, ka nɔki tʋm %2$d atikil (nam) ni.</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">M kpɛ\'ɛsidinɛ %1$s m mɛŋ, ka nɔki tʋm %2$d atikil (nam) ni.</string>
|
||||
<string name="no_uploads">Commons pʋ\'ʋsidif ken-ken!\n\nKpɛn\'ɛmi fʋ yiiga media dɔlisid fʋn na din paasim la.</string>
|
||||
<string name="no_categories_selected">Fʋ pʋ gaŋ buudsi\'a</string>
|
||||
<string name="no_depictions_selected">Fʋ pʋ gaŋ banɛ nwan taaba</string>
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@
|
|||
<string name="menu_download">Eroflueden</string>
|
||||
<string name="preference_license">Standardlizenz</string>
|
||||
<string name="use_previous">Viregen Titel a Beschreiwung benotzen</string>
|
||||
<string name="preference_theme">Faarfschema</string>
|
||||
<string name="preference_theme">Faarfscheema</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>
|
||||
|
|
@ -295,6 +295,7 @@
|
|||
<string name="continue_message">Virufueren</string>
|
||||
<string name="correct">Richteg Äntwert</string>
|
||||
<string name="wrong">Falsch Äntwert</string>
|
||||
<string name="quiz_screenshot_question">Ass et an der Rei fir dëse Screenshot eropzelueden?</string>
|
||||
<string name="share_app_title">App deelen</string>
|
||||
<string name="rotate">Dréinen</string>
|
||||
<string name="no_pictures_in_this_area">Keng Biller an dëser Géigend</string>
|
||||
|
|
@ -332,7 +333,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Ech hu gemierkt, datt et schlecht fir meng Privatsphär ass</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Ech hu meng Meenung geännert, ech wëll net méi, datt et ëffentlech siichtbar ass</string>
|
||||
<string name="deletion_reason_not_interesting">Pardon, dëst Bild ass net interessant fir eng Enzyklopedie</string>
|
||||
<string name="uploaded_by_myself">Vu mir selwer de(n) %1$s eropgelueden, benotzt a(n) %2$d Artikel(en).</string>
|
||||
<string name="uploaded_by_myself">Vu mir selwer de(n) %1$s eropgelueden, benotzt a mindestens %2$d Artikel(en).</string>
|
||||
<string name="no_categories_selected">Keng Kategorien erausgesicht</string>
|
||||
<string name="no_categories_selected_warning_desc">Biller ouni Kategorië si meeschtens net benotzbar. Sidd Dir sécher, datt Dir weider maache wëllt ouni eng Kategorie auszewielen?</string>
|
||||
<string name="back_button_warning">Eroplueden ofbriechen</string>
|
||||
|
|
@ -541,4 +542,5 @@
|
|||
<string name="vanish_account">Kont opléisen</string>
|
||||
<string name="caption">Beschrëftung</string>
|
||||
<string name="caption_copied_to_clipboard">Text an den Tëschespäicher kopéiert</string>
|
||||
<string name="nominated_for_deletion_btn">Virgeschloe fir ze läschen</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Supratau, kad tai kenkia mano privatumui</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Persigalvojau, nebenoriu, kad tai būtų viešai matoma</string>
|
||||
<string name="deletion_reason_not_interesting">Atsiprašome, ši nuotrauka neįdomi enciklopedijai</string>
|
||||
<string name="uploaded_by_myself">Įkelta mano %1$s, naudojama %2$d straipsnyje(-iuose).</string>
|
||||
<string name="uploaded_by_myself" fuzzy="true">Įkelta mano %1$s, naudojama %2$d straipsnyje(-iuose).</string>
|
||||
<string name="no_uploads">Sveiki atvykę į Vikiteką!\n\nĮkelkite pirmąjį failą bakstelėdami pridėjimo mygtuką.</string>
|
||||
<string name="no_categories_selected">Nepasirinkta jokių kategorijų</string>
|
||||
<string name="no_categories_selected_warning_desc">Vaizdai be kategorijų retai naudojami. Ar tikrai norite tęsti nepasirinkę kategorijų?</string>
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@
|
|||
<string name="deletion_reason_bad_for_my_privacy">Сфатив дека ќе ми ја наруши приватноста</string>
|
||||
<string name="deletion_reason_no_longer_want_public">Се премислив. Не сакам повеќе да биде видлива</string>
|
||||
<string name="deletion_reason_not_interesting">За жал, сликава не е соодветна за енциклопедија</string>
|
||||
<string name="uploaded_by_myself">Подигнато од мене %1$s, кое се користи во %2$d статии.</string>
|
||||
<string name="uploaded_by_myself">Подигнато од мене на %1$s, и се користи во барем {{PLURAL:%2$d|one=една статија|%2$d статии}}.</string>
|
||||
<string name="no_uploads">Добре дојдовте на Ризницата!\n\nПодигнете ја вашата прва слика или снимка допирајќи го копчето за додавање.</string>
|
||||
<string name="no_categories_selected">Нема избрано категории</string>
|
||||
<string name="no_categories_selected_warning_desc">Некатегоризираните слики се слабо употребливи. Дали сигурно сакате да продолжите без да ставите категории?</string>
|
||||
|
|
@ -825,4 +825,5 @@
|
|||
<string name="show_in_nearby">Прикажи во „Во близина“</string>
|
||||
<string name="image_tag_line_created_and_uploaded_by">Создал: %1$s</string>
|
||||
<string name="image_tag_line_created_by_and_uploaded_by">Создал %1$s, а подигнал %2$s</string>
|
||||
<string name="nominated_for_deletion_btn">Предложено за бришење</string>
|
||||
</resources>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue