Merge remote-tracking branch 'origin/Nearby-upload-not-being-linked-from-Wikidata' into Nearby-upload-not-being-linked-from-Wikidata

This commit is contained in:
Sujal-Gupta-SG 2025-02-03 23:09:21 +05:30
commit b7185073a2
49 changed files with 1242 additions and 439 deletions

View file

@ -89,7 +89,7 @@ jobs:
run: bash ./gradlew assembleBetaDebug --stacktrace run: bash ./gradlew assembleBetaDebug --stacktrace
- name: Upload betaDebug APK - name: Upload betaDebug APK
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: betaDebugAPK name: betaDebugAPK
path: app/build/outputs/apk/beta/debug/app-*.apk path: app/build/outputs/apk/beta/debug/app-*.apk
@ -98,7 +98,7 @@ jobs:
run: bash ./gradlew assembleProdDebug --stacktrace run: bash ./gradlew assembleProdDebug --stacktrace
- name: Upload prodDebug APK - name: Upload prodDebug APK
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: prodDebugAPK name: prodDebugAPK
path: app/build/outputs/apk/prod/debug/app-*.apk path: app/build/outputs/apk/prod/debug/app-*.apk

3
.gitignore vendored
View file

@ -46,4 +46,5 @@ captures/*
# Test and other output # Test and other output
app/jacoco.exec app/jacoco.exec
app/CommonsContributions app/CommonsContributions
app/.*

0
app/.attach_pid781771 Normal file
View file

View file

@ -314,6 +314,7 @@ android {
buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.org\"" buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.org\""
buildConfigField "String", "WIKIDATA_URL", "\"https://www.wikidata.org\"" buildConfigField "String", "WIKIDATA_URL", "\"https://www.wikidata.org\""
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\"" buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.org/wiki/\""
buildConfigField "String", "MOBILE_META_URL", "\"https://meta.m.wikimedia.org/wiki/\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\"" buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\"" buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\"" buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.org/wiki/Special:PasswordReset\""
@ -350,6 +351,7 @@ android {
buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.beta.wmflabs.org\"" buildConfigField "String", "COMMONS_URL", "\"https://commons.wikimedia.beta.wmflabs.org\""
buildConfigField "String", "WIKIDATA_URL", "\"https://www.wikidata.org\"" buildConfigField "String", "WIKIDATA_URL", "\"https://www.wikidata.org\""
buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\"" buildConfigField "String", "MOBILE_HOME_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/wiki/\""
buildConfigField "String", "MOBILE_META_URL", "\"https://meta.m.wikimedia.beta.wmflabs.org/wiki/\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\"" buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\"" buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\"" buildConfigField "String", "FORGOT_PASSWORD_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/Special:PasswordReset\""

View file

@ -1,187 +0,0 @@
package fr.free.nrw.commons;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import fr.free.nrw.commons.databinding.ActivityAboutBinding;
import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DialogUtil;
import java.util.Collections;
import java.util.List;
/**
* Represents about screen of this app
*/
public class AboutActivity extends BaseActivity {
/*
This View Binding class is auto-generated for each xml file. The format is usually the name
of the file with PascalCasing (The underscore characters will be ignored).
More information is available at https://developer.android.com/topic/libraries/view-binding
*/
private ActivityAboutBinding binding;
/**
* This method helps in the creation About screen
*
* @param savedInstanceState Data bundle
*/
@Override
@SuppressLint("StringFormatInvalid")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
Instead of just setting the view with the xml file. We need to use View Binding class.
*/
binding = ActivityAboutBinding.inflate(getLayoutInflater());
final View view = binding.getRoot();
setContentView(view);
setSupportActionBar(binding.toolbarBinding.toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
final String aboutText = getString(R.string.about_license);
/*
We can then access all the views by just using the id names like this.
camelCasing is used with underscore characters being ignored.
*/
binding.aboutLicense.setHtmlText(aboutText);
@SuppressLint("StringFormatMatches")
String improveText = String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL);
binding.aboutImprove.setHtmlText(improveText);
binding.aboutVersion.setText(ConfigUtils.getVersionNameWithSha(getApplicationContext()));
Utils.setUnderlinedText(binding.aboutFaq, R.string.about_faq, getApplicationContext());
Utils.setUnderlinedText(binding.aboutRateUs, R.string.about_rate_us, getApplicationContext());
Utils.setUnderlinedText(binding.aboutUserGuide, R.string.user_guide, getApplicationContext());
Utils.setUnderlinedText(binding.aboutPrivacyPolicy, R.string.about_privacy_policy, getApplicationContext());
Utils.setUnderlinedText(binding.aboutTranslate, R.string.about_translate, getApplicationContext());
Utils.setUnderlinedText(binding.aboutCredits, R.string.about_credits, getApplicationContext());
/*
To set listeners, we can create a separate method and use lambda syntax.
*/
binding.facebookLaunchIcon.setOnClickListener(this::launchFacebook);
binding.githubLaunchIcon.setOnClickListener(this::launchGithub);
binding.websiteLaunchIcon.setOnClickListener(this::launchWebsite);
binding.aboutRateUs.setOnClickListener(this::launchRatings);
binding.aboutCredits.setOnClickListener(this::launchCredits);
binding.aboutPrivacyPolicy.setOnClickListener(this::launchPrivacyPolicy);
binding.aboutUserGuide.setOnClickListener(this::launchUserGuide);
binding.aboutFaq.setOnClickListener(this::launchFrequentlyAskedQuesions);
binding.aboutTranslate.setOnClickListener(this::launchTranslate);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();
return true;
}
public void launchFacebook(View view) {
Intent intent;
try {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL));
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME);
startActivity(intent);
} catch (Exception e) {
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL));
}
}
public void launchGithub(View view) {
Intent intent;
try {
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL));
intent.setPackage(Urls.GITHUB_PACKAGE_NAME);
startActivity(intent);
} catch (Exception e) {
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL));
}
}
public void launchWebsite(View view) {
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL));
}
public void launchRatings(View view){
Utils.rateApp(this);
}
public void launchCredits(View view) {
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL));
}
public void launchUserGuide(View view) {
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL));
}
public void launchPrivacyPolicy(View view) {
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL));
}
public void launchFrequentlyAskedQuesions(View view) {
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_about, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.share_app_icon:
String shareText = String.format(getString(R.string.share_text), Urls.PLAY_STORE_URL_PREFIX + this.getPackageName());
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText);
sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via)));
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void launchTranslate(View view) {
@NonNull List<String> sortedLocalizedNamesRef = CommonsApplication.getInstance().getLanguageLookUpTable().getCanonicalNames();
Collections.sort(sortedLocalizedNamesRef);
final ArrayAdapter<String> languageAdapter = new ArrayAdapter<>(AboutActivity.this,
android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef);
final Spinner spinner = new Spinner(AboutActivity.this);
spinner.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
spinner.setAdapter(languageAdapter);
spinner.setGravity(17);
spinner.setPadding(50,0,0,0);
Runnable positiveButtonRunnable = () -> {
String langCode = CommonsApplication.getInstance().getLanguageLookUpTable().getCodes().get(spinner.getSelectedItemPosition());
Utils.handleWebUrl(AboutActivity.this, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode));
};
DialogUtil.showAlertDialog(this,
getString(R.string.about_translate_title),
getString(R.string.about_translate_message),
getString(R.string.about_translate_proceed),
getString(R.string.about_translate_cancel),
positiveButtonRunnable,
() -> {},
spinner
);
}
}

View file

@ -0,0 +1,209 @@
package fr.free.nrw.commons
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import android.widget.Spinner
import fr.free.nrw.commons.CommonsApplication.Companion.instance
import fr.free.nrw.commons.databinding.ActivityAboutBinding
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
/**
* Represents about screen of this app
*/
class AboutActivity : BaseActivity() {
/*
This View Binding class is auto-generated for each xml file. The format is usually the name
of the file with PascalCasing (The underscore characters will be ignored).
More information is available at https://developer.android.com/topic/libraries/view-binding
*/
private var binding: ActivityAboutBinding? = null
/**
* This method helps in the creation About screen
*
* @param savedInstanceState Data bundle
*/
@SuppressLint("StringFormatInvalid") //TODO:
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
/*
Instead of just setting the view with the xml file. We need to use View Binding class.
*/
binding = ActivityAboutBinding.inflate(layoutInflater)
val view: View = binding!!.root
setContentView(view)
setSupportActionBar(binding!!.toolbarBinding.toolbar)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
val aboutText = getString(R.string.about_license)
/*
We can then access all the views by just using the id names like this.
camelCasing is used with underscore characters being ignored.
*/
binding!!.aboutLicense.setHtmlText(aboutText)
@SuppressLint("StringFormatMatches") // TODO:
val improveText =
String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL)
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
)
/*
To set listeners, we can create a separate method and use lambda syntax.
*/
binding!!.facebookLaunchIcon.setOnClickListener(::launchFacebook)
binding!!.githubLaunchIcon.setOnClickListener(::launchGithub)
binding!!.websiteLaunchIcon.setOnClickListener(::launchWebsite)
binding!!.aboutRateUs.setOnClickListener(::launchRatings)
binding!!.aboutCredits.setOnClickListener(::launchCredits)
binding!!.aboutPrivacyPolicy.setOnClickListener(::launchPrivacyPolicy)
binding!!.aboutUserGuide.setOnClickListener(::launchUserGuide)
binding!!.aboutFaq.setOnClickListener(::launchFrequentlyAskedQuesions)
binding!!.aboutTranslate.setOnClickListener(::launchTranslate)
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
fun launchFacebook(view: View?) {
val intent: Intent
try {
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL))
intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME)
startActivity(intent)
} catch (e: Exception) {
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL))
}
}
fun launchGithub(view: View?) {
val intent: Intent
try {
intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL))
intent.setPackage(Urls.GITHUB_PACKAGE_NAME)
startActivity(intent)
} catch (e: Exception) {
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL))
}
}
fun launchWebsite(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL))
}
fun launchRatings(view: View?) {
Utils.rateApp(this)
}
fun launchCredits(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL))
}
fun launchUserGuide(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL))
}
fun launchPrivacyPolicy(view: View?) {
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
}
fun launchFrequentlyAskedQuesions(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL))
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_about, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.share_app_icon -> {
val shareText = String.format(
getString(R.string.share_text),
Urls.PLAY_STORE_URL_PREFIX + this.packageName
)
val sendIntent = Intent()
sendIntent.setAction(Intent.ACTION_SEND)
sendIntent.putExtra(Intent.EXTRA_TEXT, shareText)
sendIntent.setType("text/plain")
startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via)))
return true
}
else -> return super.onOptionsItemSelected(item)
}
}
fun launchTranslate(view: View?) {
val sortedLocalizedNamesRef = instance.languageLookUpTable!!.getCanonicalNames()
Collections.sort(sortedLocalizedNamesRef)
val languageAdapter = ArrayAdapter(
this@AboutActivity,
android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef
)
val spinner = Spinner(this@AboutActivity)
spinner.layoutParams =
LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
spinner.adapter = languageAdapter
spinner.gravity = 17
spinner.setPadding(50, 0, 0, 0)
val positiveButtonRunnable = Runnable {
val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition]
Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode))
}
showAlertDialog(
this,
getString(R.string.about_translate_title),
getString(R.string.about_translate_message),
getString(R.string.about_translate_proceed),
getString(R.string.about_translate_cancel),
positiveButtonRunnable,
{},
spinner
)
}
}

View file

@ -148,13 +148,27 @@ public class Utils {
} }
/** /**
* Util function to handle geo coordinates * Util function to handle geo coordinates. It no longer depends on google maps and any app
* It no longer depends on google maps and any app capable of handling the map intent can handle it * capable of handling the map intent can handle it
* @param context *
* @param latLng * @param context The context for launching intent
* @param latLng The latitude and longitude of the location
*/ */
public static void handleGeoCoordinates(Context context, LatLng latLng) { public static void handleGeoCoordinates(final Context context, final LatLng latLng) {
Intent mapIntent = new Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri()); 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) { if (mapIntent.resolveActivity(context.getPackageManager()) != null) {
context.startActivity(mapIntent); context.startActivity(mapIntent);
} else { } else {

View file

@ -28,6 +28,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.CommonsApplication.ActivityLogoutListener
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.di.ApplicationlessInjection import fr.free.nrw.commons.di.ApplicationlessInjection
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
@ -85,7 +87,12 @@ class SingleWebViewActivity : ComponentActivity() {
url = url, url = url,
successUrl = successUrl, successUrl = successUrl,
onSuccess = { onSuccess = {
// TODO Redirect the user to login screen like we do when the user logout's //Redirect the user to login screen like we do when the user logout's
val app = applicationContext as CommonsApplication
app.clearApplicationData(
applicationContext,
ActivityLogoutListener(activity = this, ctx = applicationContext)
)
finish() finish()
}, },
modifier = Modifier modifier = Modifier

View file

@ -36,37 +36,35 @@ class CategoriesModel
* @return * @return
*/ */
fun isSpammyCategory(item: String): Boolean { fun isSpammyCategory(item: String): Boolean {
// Check for current and previous year to exclude these categories from removal
val now = Calendar.getInstance()
val curYear = now[Calendar.YEAR]
val curYearInString = curYear.toString()
val prevYear = curYear - 1
val prevYearInString = prevYear.toString()
Timber.d("Previous year: %s", prevYearInString)
val mentionsDecade = item.matches(".*0s.*".toRegex())
val recentDecade = item.matches(".*20[0-2]0s.*".toRegex())
val spammyCategory =
item.matches("(.*)needing(.*)".toRegex()) ||
item.matches("(.*)taken on(.*)".toRegex())
// always skip irrelevant categories such as Media_needing_categories_as_of_16_June_2017(Issue #750) // always skip irrelevant categories such as Media_needing_categories_as_of_16_June_2017(Issue #750)
val spammyCategory = item.matches("(.*)needing(.*)".toRegex())
|| item.matches("(.*)taken on(.*)".toRegex())
// checks for
// dd/mm/yyyy or yy
// yyyy or yy/mm/dd
// yyyy or yy/mm
// mm/yyyy or yy
// for `yy` it is assumed that 20XX is implicit.
// with separators [., /, -]
val isIrrelevantCategory =
item.contains("""\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}|\d{2,4}[-/.]\d{1,2}[-/.]\d{1,2}|\d{2,4}[-/.]\d{1,2}|\d{1,2}[-/.]\d{2,4}""".toRegex())
if (spammyCategory) { if (spammyCategory) {
return true return true
} }
if (mentionsDecade) { if(isIrrelevantCategory){
// Check if the year in the form of XX(X)0s is recent/relevant, i.e. in the 2000s or 2010s/2020s as stated in Issue #1029 return true
// Example: "2020s" is OK, but "1920s" is not (and should be skipped)
return !recentDecade
} else {
// If it is not an year in decade form (e.g. 19xxs/20xxs), then check if item contains a 4-digit year
// anywhere within the string (.* is wildcard) (Issue #47)
// And that item does not equal the current year or previous year
return item.matches(".*(19|20)\\d{2}.*".toRegex()) &&
!item.contains(curYearInString) &&
!item.contains(prevYearInString)
} }
val hasYear = item.matches("(.*\\d{4}.*)".toRegex())
val validYearsRange = item.matches(".*(20[0-9]{2}).*".toRegex())
// finally if there's 4 digits year exists in XXXX it should only be in 20XX range.
return hasYear && !validYearsRange
} }
/** /**

View file

@ -207,6 +207,9 @@ public class MainActivity extends BaseActivity
private boolean loadFragment(Fragment fragment, boolean showBottom) { private boolean loadFragment(Fragment fragment, boolean showBottom) {
//showBottom so that we do not show the bottom tray again when constructing //showBottom so that we do not show the bottom tray again when constructing
//from the saved instance state. //from the saved instance state.
freeUpFragments();
if (fragment instanceof ContributionsFragment) { if (fragment instanceof ContributionsFragment) {
if (activeFragment == ActiveFragment.CONTRIBUTIONS) { if (activeFragment == ActiveFragment.CONTRIBUTIONS) {
// scroll to top if already on the Contributions tab // scroll to top if already on the Contributions tab
@ -256,6 +259,31 @@ public class MainActivity extends BaseActivity
return false; return false;
} }
/**
* loadFragment() overload that supports passing extras to fragments
**/
private boolean loadFragment(Fragment fragment, boolean showBottom, Bundle args) {
if (fragment != null && args != null) {
fragment.setArguments(args);
}
return loadFragment(fragment, showBottom);
}
/**
* Old implementation of loadFragment() was causing memory leaks, due to MainActivity holding
* references to cleared fragments. This function frees up all fragment references.
* <p>
* Called in loadFragment() before doing the actual loading.
**/
public void freeUpFragments() {
// free all fragments except contributionsFragment because several tests depend on it.
// hence, contributionsFragment is probably still a leak
nearbyParentFragment = null;
exploreFragment = null;
bookmarkFragment = null;
}
public void hideTabs() { public void hideTabs() {
binding.fragmentMainNavTabLayout.setVisibility(View.GONE); binding.fragmentMainNavTabLayout.setVisibility(View.GONE);
} }
@ -432,6 +460,42 @@ public class MainActivity extends BaseActivity
}); });
} }
/**
* Launch the Explore fragment from Nearby fragment. This method is called when a user clicks
* the 'Show in Explore' option in the 3-dots menu in Nearby.
*
* @param zoom current zoom of Nearby map
* @param latitude current latitude of Nearby map
* @param longitude current longitude of Nearby map
**/
public void loadExploreMapFromNearby(double zoom, double latitude, double longitude) {
Bundle bundle = new Bundle();
bundle.putDouble("prev_zoom", zoom);
bundle.putDouble("prev_latitude", latitude);
bundle.putDouble("prev_longitude", longitude);
loadFragment(ExploreFragment.newInstance(), false, bundle);
setSelectedItemId(NavTab.EXPLORE.code());
}
/**
* Launch the Nearby fragment from Explore fragment. This method is called when a user clicks
* the 'Show in Nearby' option in the 3-dots menu in Explore.
*
* @param zoom current zoom of Explore map
* @param latitude current latitude of Explore map
* @param longitude current longitude of Explore map
**/
public void loadNearbyMapFromExplore(double zoom, double latitude, double longitude) {
Bundle bundle = new Bundle();
bundle.putDouble("prev_zoom", zoom);
bundle.putDouble("prev_latitude", latitude);
bundle.putDouble("prev_longitude", longitude);
loadFragment(NearbyParentFragment.newInstance(), false, bundle);
setSelectedItemId(NavTab.NEARBY.code());
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();

View file

@ -24,6 +24,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.TreeMap import java.util.TreeMap
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -103,6 +104,18 @@ class ImageAdapter(
*/ */
private var imagePositionAsPerIncreasingOrder = 0 private var imagePositionAsPerIncreasingOrder = 0
/**
* Stores the number of images currently visible on the screen
*/
private val _currentImagesCount = MutableStateFlow(0)
val currentImagesCount = _currentImagesCount
/**
* Stores whether images are being loaded or not
*/
private val _isLoadingImages = MutableStateFlow(false)
val isLoadingImages = _isLoadingImages
/** /**
* Coroutine Dispatchers and Scope. * Coroutine Dispatchers and Scope.
*/ */
@ -184,8 +197,12 @@ class ImageAdapter(
// If the position is not already visited, that means the position is new then // If the position is not already visited, that means the position is new then
// finds the next actionable image position from all images // finds the next actionable image position from all images
if (!alreadyAddedPositions.contains(position)) { if (!alreadyAddedPositions.contains(position)) {
processThumbnailForActionedImage(holder, position, uploadingContributionList) processThumbnailForActionedImage(
holder,
position,
uploadingContributionList
)
_isLoadingImages.value = false
// If the position is already visited, that means the image is already present // If the position is already visited, that means the image is already present
// inside map, so it will fetch the image from the map and load in the holder // inside map, so it will fetch the image from the map and load in the holder
} else { } else {
@ -231,6 +248,7 @@ class ImageAdapter(
position: Int, position: Int,
uploadingContributionList: List<Contribution>, uploadingContributionList: List<Contribution>,
) { ) {
_isLoadingImages.value = true
val next = val next =
imageLoader.nextActionableImage( imageLoader.nextActionableImage(
allImages, allImages,
@ -252,6 +270,7 @@ class ImageAdapter(
actionableImagesMap[next] = allImages[next] actionableImagesMap[next] = allImages[next]
alreadyAddedPositions.add(imagePositionAsPerIncreasingOrder) alreadyAddedPositions.add(imagePositionAsPerIncreasingOrder)
imagePositionAsPerIncreasingOrder++ imagePositionAsPerIncreasingOrder++
_currentImagesCount.value = imagePositionAsPerIncreasingOrder
Glide Glide
.with(holder.image) .with(holder.image)
.load(allImages[next].uri) .load(allImages[next].uri)
@ -267,6 +286,7 @@ class ImageAdapter(
reachedEndOfFolder = true reachedEndOfFolder = true
notifyItemRemoved(position) notifyItemRemoved(position)
} }
_isLoadingImages.value = false
} }
/** /**
@ -372,6 +392,7 @@ class ImageAdapter(
emptyMap: TreeMap<Int, Image>, emptyMap: TreeMap<Int, Image>,
uploadedImages: List<Contribution> = ArrayList(), uploadedImages: List<Contribution> = ArrayList(),
) { ) {
_isLoadingImages.value = true
allImages = fixedImages allImages = fixedImages
val oldImageList: ArrayList<Image> = images val oldImageList: ArrayList<Image> = images
val newImageList: ArrayList<Image> = ArrayList(newImages) val newImageList: ArrayList<Image> = ArrayList(newImages)
@ -382,6 +403,7 @@ class ImageAdapter(
reachedEndOfFolder = false reachedEndOfFolder = false
selectedImages = ArrayList() selectedImages = ArrayList()
imagePositionAsPerIncreasingOrder = 0 imagePositionAsPerIncreasingOrder = 0
_currentImagesCount.value = imagePositionAsPerIncreasingOrder
val diffResult = val diffResult =
DiffUtil.calculateDiff( DiffUtil.calculateDiff(
ImagesDiffCallback(oldImageList, newImageList), ImagesDiffCallback(oldImageList, newImageList),
@ -441,6 +463,7 @@ class ImageAdapter(
val entry = iterator.next() val entry = iterator.next()
if (entry.value == image) { if (entry.value == image) {
imagePositionAsPerIncreasingOrder -= 1 imagePositionAsPerIncreasingOrder -= 1
_currentImagesCount.value = imagePositionAsPerIncreasingOrder
iterator.remove() iterator.remove()
alreadyAddedPositions.removeAt(alreadyAddedPositions.size - 1) alreadyAddedPositions.removeAt(alreadyAddedPositions.size - 1)
notifyItemRemoved(index) notifyItemRemoved(index)

View file

@ -12,8 +12,12 @@ import android.widget.ProgressBar
import android.widget.Switch import android.widget.Switch
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
@ -38,6 +42,10 @@ import fr.free.nrw.commons.theme.BaseActivity
import fr.free.nrw.commons.upload.FileProcessor import fr.free.nrw.commons.upload.FileProcessor
import fr.free.nrw.commons.upload.FileUtilsWrapper import fr.free.nrw.commons.upload.FileUtilsWrapper
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import java.util.TreeMap import java.util.TreeMap
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -80,6 +88,12 @@ class ImageFragment :
*/ */
var allImages: ArrayList<Image> = ArrayList() var allImages: ArrayList<Image> = ArrayList()
/**
* Keeps track of switch state
*/
private val _switchState = MutableStateFlow(false)
val switchState = _switchState.asStateFlow()
/** /**
* View model Factory. * View model Factory.
*/ */
@ -214,7 +228,11 @@ class ImageFragment :
switch = binding?.switchWidget switch = binding?.switchWidget
switch?.visibility = View.VISIBLE switch?.visibility = View.VISIBLE
switch?.setOnCheckedChangeListener { _, isChecked -> onChangeSwitchState(isChecked) } _switchState.value = switch?.isChecked ?: false
switch?.setOnCheckedChangeListener { _, isChecked ->
onChangeSwitchState(isChecked)
_switchState.value = isChecked
}
selectorRV = binding?.selectorRv selectorRV = binding?.selectorRv
loader = binding?.loader loader = binding?.loader
progressLayout = binding?.progressLayout progressLayout = binding?.progressLayout
@ -234,6 +252,28 @@ class ImageFragment :
return binding?.root return binding?.root
} }
/**
* onViewCreated
* Updates empty text view visibility based on image count, switch state, and loading status.
*/
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
combine(
imageAdapter.currentImagesCount,
switchState,
imageAdapter.isLoadingImages
) { imageCount, isChecked, isLoadingImages ->
Triple(imageCount, isChecked, isLoadingImages)
}.collect { (imageCount, isChecked, isLoadingImages) ->
binding?.allImagesUploadedOrMarked?.isVisible =
!isLoadingImages && !isChecked && imageCount == 0 && (switch?.isVisible == true)
}
}
}
}
private fun onChangeSwitchState(checked: Boolean) { private fun onChangeSwitchState(checked: Boolean) {
if (checked) { if (checked) {
showAlreadyActionedImages = true showAlreadyActionedImages = true

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.explore; package fr.free.nrw.commons.explore;
import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@ -42,9 +44,13 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
@Named("default_preferences") @Named("default_preferences")
public JsonKvStore applicationKvStore; public JsonKvStore applicationKvStore;
public void setScroll(boolean canScroll){ // Nearby map state (for if we came from Nearby fragment)
if (binding != null) private double prevZoom;
{ private double prevLatitude;
private double prevLongitude;
public void setScroll(boolean canScroll) {
if (binding != null) {
binding.viewPager.setCanScroll(canScroll); binding.viewPager.setCanScroll(canScroll);
} }
} }
@ -60,6 +66,7 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
loadNearbyMapData();
binding = FragmentExploreBinding.inflate(inflater, container, false); binding = FragmentExploreBinding.inflate(inflater, container, false);
viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager()); viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
@ -89,6 +96,11 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
}); });
setTabs(); setTabs();
setHasOptionsMenu(true); setHasOptionsMenu(true);
// if we came from 'Show in Explore' in Nearby, jump to Map tab
if (isCameFromNearbyMap()) {
binding.viewPager.setCurrentItem(2);
}
return binding.getRoot(); return binding.getRoot();
} }
@ -108,6 +120,13 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
Bundle mapArguments = new Bundle(); Bundle mapArguments = new Bundle();
mapArguments.putString("categoryName", EXPLORE_MAP); mapArguments.putString("categoryName", EXPLORE_MAP);
// if we came from 'Show in Explore' in Nearby, pass on zoom and center to Explore map root
if (isCameFromNearbyMap()) {
mapArguments.putDouble("prev_zoom", prevZoom);
mapArguments.putDouble("prev_latitude", prevLatitude);
mapArguments.putDouble("prev_longitude", prevLongitude);
}
featuredRootFragment = new ExploreListRootFragment(featuredArguments); featuredRootFragment = new ExploreListRootFragment(featuredArguments);
mobileRootFragment = new ExploreListRootFragment(mobileArguments); mobileRootFragment = new ExploreListRootFragment(mobileArguments);
mapRootFragment = new ExploreMapRootFragment(mapArguments); mapRootFragment = new ExploreMapRootFragment(mapArguments);
@ -120,13 +139,35 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
fragmentList.add(mapRootFragment); fragmentList.add(mapRootFragment);
titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT)); titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT));
((MainActivity)getActivity()).showTabs(); ((MainActivity) getActivity()).showTabs();
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false); ((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.setTabData(fragmentList, titleList);
viewPagerAdapter.notifyDataSetChanged(); viewPagerAdapter.notifyDataSetChanged();
} }
/**
* Fetch Nearby map camera data from fragment arguments if any.
*/
public void loadNearbyMapData() {
// get fragment arguments
if (getArguments() != null) {
prevZoom = getArguments().getDouble("prev_zoom");
prevLatitude = getArguments().getDouble("prev_latitude");
prevLongitude = getArguments().getDouble("prev_longitude");
}
}
/**
* Checks if fragment arguments contain data from Nearby map. if present, then the user
* navigated from Nearby using 'Show in Explore'.
*
* @return true if user navigated from Nearby map
**/
public boolean isCameFromNearbyMap() {
return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0;
}
public boolean onBackPressed() { public boolean onBackPressed() {
if (binding.tabLayout.getSelectedTabPosition() == 0) { if (binding.tabLayout.getSelectedTabPosition() == 0) {
if (featuredRootFragment.backPressed()) { if (featuredRootFragment.backPressed()) {
@ -155,7 +196,38 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
*/ */
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_search, menu); // if logged in 'Show in Nearby' menu item is visible
if (applicationKvStore.getBoolean("login_skipped") == false) {
inflater.inflate(R.menu.explore_fragment_menu, menu);
MenuItem others = menu.findItem(R.id.list_item_show_in_nearby);
if (binding.viewPager.getCurrentItem() == 2) {
others.setVisible(true);
}
// if on Map tab, show all menu options, else only show search
binding.viewPager.addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
others.setVisible((position == 2));
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == SCROLL_STATE_IDLE && binding.viewPager.getCurrentItem() == 2) {
onPageSelected(2);
}
}
});
} else {
inflater.inflate(R.menu.menu_search, menu);
}
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@ -171,6 +243,9 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
case R.id.action_search: case R.id.action_search:
ActivityUtils.startActivityWithFlags(getActivity(), SearchActivity.class); ActivityUtils.startActivityWithFlags(getActivity(), SearchActivity.class);
return true; return true;
case R.id.list_item_show_in_nearby:
mapRootFragment.loadNearbyMapFromExplore();
return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }

View file

@ -39,10 +39,22 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme
} }
public ExploreMapRootFragment(Bundle bundle) { public ExploreMapRootFragment(Bundle bundle) {
// get fragment arguments
String title = bundle.getString("categoryName"); String title = bundle.getString("categoryName");
double zoom = bundle.getDouble("prev_zoom");
double latitude = bundle.getDouble("prev_latitude");
double longitude = bundle.getDouble("prev_longitude");
mapFragment = new ExploreMapFragment(); mapFragment = new ExploreMapFragment();
Bundle featuredArguments = new Bundle(); Bundle featuredArguments = new Bundle();
featuredArguments.putString("categoryName", title); featuredArguments.putString("categoryName", title);
// if we came from 'Show in Explore' in Nearby, pass on zoom and center
if (zoom != 0.0 || latitude != 0.0 || longitude != 0.0) {
featuredArguments.putDouble("prev_zoom", zoom);
featuredArguments.putDouble("prev_latitude", latitude);
featuredArguments.putDouble("prev_longitude", longitude);
}
mapFragment.setArguments(featuredArguments); mapFragment.setArguments(featuredArguments);
} }
@ -198,7 +210,8 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme
((MainActivity) getActivity()).showTabs(); ((MainActivity) getActivity()).showTabs();
return true; return true;
} if (mapFragment != null && mapFragment.isVisible()) { }
if (mapFragment != null && mapFragment.isVisible()) {
if (mapFragment.backButtonClicked()) { if (mapFragment.backButtonClicked()) {
// Explore map fragment handled the event no further action required. // Explore map fragment handled the event no further action required.
return true; return true;
@ -213,6 +226,10 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme
return false; return false;
} }
public void loadNearbyMapFromExplore() {
mapFragment.loadNearbyMapFromExplore();
}
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();

View file

@ -38,6 +38,7 @@ import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.databinding.FragmentExploreMapBinding; import fr.free.nrw.commons.databinding.FragmentExploreMapBinding;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.ExploreMapRootFragment; import fr.free.nrw.commons.explore.ExploreMapRootFragment;
@ -115,6 +116,11 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
SystemThemeUtils systemThemeUtils; SystemThemeUtils systemThemeUtils;
LocationPermissionsHelper locationPermissionsHelper; LocationPermissionsHelper locationPermissionsHelper;
// Nearby map state (if we came from Nearby)
private double prevZoom;
private double prevLatitude;
private double prevLongitude;
private ExploreMapPresenter presenter; private ExploreMapPresenter presenter;
public FragmentExploreMapBinding binding; public FragmentExploreMapBinding binding;
@ -160,6 +166,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
ViewGroup container, ViewGroup container,
Bundle savedInstanceState Bundle savedInstanceState
) { ) {
loadNearbyMapData();
binding = FragmentExploreMapBinding.inflate(getLayoutInflater()); binding = FragmentExploreMapBinding.inflate(getLayoutInflater());
return binding.getRoot(); return binding.getRoot();
} }
@ -169,12 +176,14 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
setSearchThisAreaButtonVisibility(false); setSearchThisAreaButtonVisibility(false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); binding.tvAttribution.setText(
Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY));
} else { } else {
binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution)));
} }
initNetworkBroadCastReceiver(); initNetworkBroadCastReceiver();
locationPermissionsHelper = new LocationPermissionsHelper(getActivity(),locationManager,this); locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager,
this);
if (presenter == null) { if (presenter == null) {
presenter = new ExploreMapPresenter(bookmarkLocationDao); presenter = new ExploreMapPresenter(bookmarkLocationDao);
} }
@ -204,9 +213,14 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
scaleBarOverlay.setBackgroundPaint(barPaint); scaleBarOverlay.setBackgroundPaint(barPaint);
scaleBarOverlay.enableScaleBar(); scaleBarOverlay.enableScaleBar();
binding.mapView.getOverlays().add(scaleBarOverlay); binding.mapView.getOverlays().add(scaleBarOverlay);
binding.mapView.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER); binding.mapView.getZoomController()
.setVisibility(CustomZoomButtonsController.Visibility.NEVER);
binding.mapView.setMultiTouchControls(true); binding.mapView.setMultiTouchControls(true);
binding.mapView.getController().setZoom(ZOOM_LEVEL);
if (!isCameFromNearbyMap()) {
binding.mapView.getController().setZoom(ZOOM_LEVEL);
}
performMapReadyActions(); performMapReadyActions();
binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() {
@ -295,7 +309,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
unregisterNetworkReceiver(); unregisterNetworkReceiver();
} }
/** /**
* Unregisters the networkReceiver * Unregisters the networkReceiver
*/ */
@ -328,11 +342,51 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
isPermissionDenied = true; isPermissionDenied = true;
} }
lastKnownLocation = MapUtils.getDefaultLatLng(); lastKnownLocation = MapUtils.getDefaultLatLng();
moveCameraToPosition(
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); // if we came from 'Show in Explore' in Nearby, load Nearby map center and zoom
if (isCameFromNearbyMap()) {
moveCameraToPosition(
new GeoPoint(prevLatitude, prevLongitude),
prevZoom,
1L
);
} else {
moveCameraToPosition(
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
}
presenter.onMapReady(exploreMapController); presenter.onMapReady(exploreMapController);
} }
/**
* Fetch Nearby map camera data from fragment arguments if any.
*/
public void loadNearbyMapData() {
// get fragment arguments
if (getArguments() != null) {
prevZoom = getArguments().getDouble("prev_zoom");
prevLatitude = getArguments().getDouble("prev_latitude");
prevLongitude = getArguments().getDouble("prev_longitude");
}
}
/**
* Checks if fragment arguments contain data from Nearby map, indicating that the user navigated
* from Nearby using 'Show in Explore'.
*
* @return true if user navigated from Nearby map
**/
public boolean isCameFromNearbyMap() {
return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0;
}
public void loadNearbyMapFromExplore() {
((MainActivity) getContext()).loadNearbyMapFromExplore(
binding.mapView.getZoomLevelDouble(),
binding.mapView.getMapCenter().getLatitude(),
binding.mapView.getMapCenter().getLongitude()
);
}
private void initViews() { private void initViews() {
Timber.d("init views called"); Timber.d("init views called");
initBottomSheets(); initBottomSheets();
@ -346,7 +400,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
*/ */
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private void initBottomSheets() { private void initBottomSheets() {
bottomSheetDetailsBehavior = BottomSheetBehavior.from(binding.bottomSheetDetailsBinding.getRoot()); bottomSheetDetailsBehavior = BottomSheetBehavior.from(
binding.bottomSheetDetailsBinding.getRoot());
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
binding.bottomSheetDetailsBinding.getRoot().setVisibility(View.VISIBLE); binding.bottomSheetDetailsBinding.getRoot().setVisibility(View.VISIBLE);
} }
@ -404,7 +459,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
if (currentLatLng == null) { if (currentLatLng == null) {
return; return;
} }
if (currentLatLng.equals(getLastMapFocus())) { // Means we are checking around current location if (currentLatLng.equals(
getLastMapFocus())) { // Means we are checking around current location
nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(currentLatLng, nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(currentLatLng,
getLastMapFocus(), true); getLastMapFocus(), true);
} else { } else {
@ -416,11 +472,12 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(explorePlacesInfo -> { .subscribe(explorePlacesInfo -> {
mediaList = explorePlacesInfo.mediaList; mediaList = explorePlacesInfo.mediaList;
if(mediaList == null) { if (mediaList == null) {
showResponseMessage(getString(R.string.no_pictures_in_this_area)); showResponseMessage(getString(R.string.no_pictures_in_this_area));
} }
updateMapMarkers(explorePlacesInfo); updateMapMarkers(explorePlacesInfo);
lastMapFocus = new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude()); lastMapFocus = new GeoPoint(currentLatLng.getLatitude(),
currentLatLng.getLongitude());
}, },
throwable -> { throwable -> {
Timber.d(throwable); Timber.d(throwable);
@ -474,9 +531,9 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER);
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
setProgressBarVisibility(true); setProgressBarVisibility(true);
} } else {
else { locationPermissionsHelper.showLocationOffDialog(getActivity(),
locationPermissionsHelper.showLocationOffDialog(getActivity(), R.string.ask_to_turn_location_on_text); R.string.ask_to_turn_location_on_text);
} }
presenter.onMapReady(exploreMapController); presenter.onMapReady(exploreMapController);
registerUnregisterLocationListener(false); registerUnregisterLocationListener(false);
@ -508,7 +565,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
recenterToUserLocation = true; recenterToUserLocation = true;
return; return;
} }
recenterMarkerToPosition(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); recenterMarkerToPosition(
new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude()));
binding.mapView.getController() binding.mapView.getController()
.animateTo(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); .animateTo(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude()));
if (lastMapFocus != null) { if (lastMapFocus != null) {
@ -545,10 +603,12 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
* @param place Place of clicked nearby marker * @param place Place of clicked nearby marker
*/ */
private void passInfoToSheet(final Place place) { private void passInfoToSheet(final Place place) {
binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getActivity(), binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener(
place.getLocation())); view -> Utils.handleGeoCoordinates(getActivity(),
place.getLocation(), binding.mapView.getZoomLevelDouble()));
binding.bottomSheetDetailsBinding.commonsButton.setVisibility(place.hasCommonsLink() ? View.VISIBLE : View.GONE); binding.bottomSheetDetailsBinding.commonsButton.setVisibility(
place.hasCommonsLink() ? View.VISIBLE : View.GONE);
binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener(
view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
@ -562,7 +622,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
} }
index++; index++;
} }
binding.bottomSheetDetailsBinding.title.setText(place.name.substring(5, place.name.lastIndexOf("."))); binding.bottomSheetDetailsBinding.title.setText(
place.name.substring(5, place.name.lastIndexOf(".")));
binding.bottomSheetDetailsBinding.category.setText(place.distance); binding.bottomSheetDetailsBinding.category.setText(place.distance);
// Remove label since it is double information // Remove label since it is double information
String descriptionText = place.getLongDescription() String descriptionText = place.getLongDescription()
@ -640,40 +701,43 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
* @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be added. * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be added.
*/ */
private void addMarkerToMap(BaseMarker nearbyBaseMarker) { private void addMarkerToMap(BaseMarker nearbyBaseMarker) {
ArrayList<OverlayItem> items = new ArrayList<>(); if (isAttachedToActivity()) {
Bitmap icon = nearbyBaseMarker.getIcon(); ArrayList<OverlayItem> items = new ArrayList<>();
Drawable d = new BitmapDrawable(getResources(), icon); Bitmap icon = nearbyBaseMarker.getIcon();
GeoPoint point = new GeoPoint( Drawable d = new BitmapDrawable(getResources(), icon);
nearbyBaseMarker.getPlace().location.getLatitude(), GeoPoint point = new GeoPoint(
nearbyBaseMarker.getPlace().location.getLongitude()); nearbyBaseMarker.getPlace().location.getLatitude(),
OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null, nearbyBaseMarker.getPlace().location.getLongitude());
point); OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null,
item.setMarker(d); point);
items.add(item); item.setMarker(d);
ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items, items.add(item);
new OnItemGestureListener<OverlayItem>() { ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items,
@Override new OnItemGestureListener<OverlayItem>() {
public boolean onItemSingleTapUp(int index, OverlayItem item) { @Override
final Place place = nearbyBaseMarker.getPlace(); public boolean onItemSingleTapUp(int index, OverlayItem item) {
if (clickedMarker != null) { final Place place = nearbyBaseMarker.getPlace();
removeMarker(clickedMarker); if (clickedMarker != null) {
addMarkerToMap(clickedMarker); removeMarker(clickedMarker);
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); addMarkerToMap(clickedMarker);
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
bottomSheetDetailsBehavior.setState(
BottomSheetBehavior.STATE_COLLAPSED);
}
clickedMarker = nearbyBaseMarker;
passInfoToSheet(place);
return true;
} }
clickedMarker = nearbyBaseMarker;
passInfoToSheet(place);
return true;
}
@Override @Override
public boolean onItemLongPress(int index, OverlayItem item) { public boolean onItemLongPress(int index, OverlayItem item) {
return false; return false;
} }
}, getContext()); }, getContext());
overlay.setFocusItemsOnTap(true); overlay.setFocusItemsOnTap(true);
binding.mapView.getOverlays().add(overlay); // Add the overlay to the map binding.mapView.getOverlays().add(overlay); // Add the overlay to the map
}
} }
/** /**
@ -707,68 +771,72 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
*/ */
@Override @Override
public void clearAllMarkers() { public void clearAllMarkers() {
binding.mapView.getOverlayManager().clear(); if (isAttachedToActivity()) {
GeoPoint geoPoint = mapCenter; binding.mapView.getOverlayManager().clear();
if (geoPoint != null) { GeoPoint geoPoint = mapCenter;
List<Overlay> overlays = binding.mapView.getOverlays(); if (geoPoint != null) {
ScaleDiskOverlay diskOverlay = List<Overlay> overlays = binding.mapView.getOverlays();
new ScaleDiskOverlay(this.getContext(), ScaleDiskOverlay diskOverlay =
geoPoint, 2000, GeoConstants.UnitOfMeasure.foot); new ScaleDiskOverlay(this.getContext(),
Paint circlePaint = new Paint(); geoPoint, 2000, GeoConstants.UnitOfMeasure.foot);
circlePaint.setColor(Color.rgb(128, 128, 128)); Paint circlePaint = new Paint();
circlePaint.setStyle(Paint.Style.STROKE); circlePaint.setColor(Color.rgb(128, 128, 128));
circlePaint.setStrokeWidth(2f); circlePaint.setStyle(Paint.Style.STROKE);
diskOverlay.setCirclePaint2(circlePaint); circlePaint.setStrokeWidth(2f);
Paint diskPaint = new Paint(); diskOverlay.setCirclePaint2(circlePaint);
diskPaint.setColor(Color.argb(40, 128, 128, 128)); Paint diskPaint = new Paint();
diskPaint.setStyle(Paint.Style.FILL_AND_STROKE); diskPaint.setColor(Color.argb(40, 128, 128, 128));
diskOverlay.setCirclePaint1(diskPaint); diskPaint.setStyle(Paint.Style.FILL_AND_STROKE);
diskOverlay.setDisplaySizeMin(900); diskOverlay.setCirclePaint1(diskPaint);
diskOverlay.setDisplaySizeMax(1700); diskOverlay.setDisplaySizeMin(900);
binding.mapView.getOverlays().add(diskOverlay); diskOverlay.setDisplaySizeMax(1700);
org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker( binding.mapView.getOverlays().add(diskOverlay);
binding.mapView); org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker(
startMarker.setPosition(geoPoint); binding.mapView);
startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, startMarker.setPosition(geoPoint);
org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM); startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER,
startMarker.setIcon( org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM);
ContextCompat.getDrawable(this.getContext(), R.drawable.current_location_marker)); startMarker.setIcon(
startMarker.setTitle("Your Location"); ContextCompat.getDrawable(this.getContext(),
startMarker.setTextLabelFontSize(24); R.drawable.current_location_marker));
binding.mapView.getOverlays().add(startMarker); startMarker.setTitle("Your Location");
} startMarker.setTextLabelFontSize(24);
ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(binding.mapView); binding.mapView.getOverlays().add(startMarker);
scaleBarOverlay.setScaleBarOffset(15, 25);
Paint barPaint = new Paint();
barPaint.setARGB(200, 255, 250, 250);
scaleBarOverlay.setBackgroundPaint(barPaint);
scaleBarOverlay.enableScaleBar();
binding.mapView.getOverlays().add(scaleBarOverlay);
binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() {
@Override
public boolean singleTapConfirmedHelper(GeoPoint p) {
if (clickedMarker != null) {
removeMarker(clickedMarker);
addMarkerToMap(clickedMarker);
binding.mapView.invalidate();
} else {
Timber.e("CLICKED MARKER IS NULL");
}
if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
// Back should first hide the bottom sheet if it is expanded
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
} else if (isDetailsBottomSheetVisible()) {
hideBottomDetailsSheet();
}
return true;
} }
ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(binding.mapView);
scaleBarOverlay.setScaleBarOffset(15, 25);
Paint barPaint = new Paint();
barPaint.setARGB(200, 255, 250, 250);
scaleBarOverlay.setBackgroundPaint(barPaint);
scaleBarOverlay.enableScaleBar();
binding.mapView.getOverlays().add(scaleBarOverlay);
binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() {
@Override
public boolean singleTapConfirmedHelper(GeoPoint p) {
if (clickedMarker != null) {
removeMarker(clickedMarker);
addMarkerToMap(clickedMarker);
binding.mapView.invalidate();
} else {
Timber.e("CLICKED MARKER IS NULL");
}
if (bottomSheetDetailsBehavior.getState()
== BottomSheetBehavior.STATE_EXPANDED) {
// Back should first hide the bottom sheet if it is expanded
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
} else if (isDetailsBottomSheetVisible()) {
hideBottomDetailsSheet();
}
return true;
}
@Override @Override
public boolean longPressHelper(GeoPoint p) { public boolean longPressHelper(GeoPoint p) {
return false; return false;
} }
})); }));
binding.mapView.setMultiTouchControls(true); binding.mapView.setMultiTouchControls(true);
}
} }
/** /**
@ -825,6 +893,18 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
binding.mapView.getController().animateTo(geoPoint); binding.mapView.getController().animateTo(geoPoint);
} }
/**
* Moves the camera of the map view to the specified GeoPoint at specified zoom level and speed
* using an animation.
*
* @param geoPoint The GeoPoint representing the new camera position for the map.
* @param zoom Zoom level of the map camera
* @param speed Speed of animation
*/
private void moveCameraToPosition(GeoPoint geoPoint, double zoom, long speed) {
binding.mapView.getController().animateTo(geoPoint, zoom, speed);
}
@Override @Override
public fr.free.nrw.commons.location.LatLng getLastMapFocus() { public fr.free.nrw.commons.location.LatLng getLastMapFocus() {
return lastMapFocus == null ? getMapCenter() : new fr.free.nrw.commons.location.LatLng( return lastMapFocus == null ? getMapCenter() : new fr.free.nrw.commons.location.LatLng(
@ -850,14 +930,17 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
-0.07483536015053005, 1f); -0.07483536015053005, 1f);
} }
} }
moveCameraToPosition(new GeoPoint(latLnge.getLatitude(),latLnge.getLongitude())); if (!isCameFromNearbyMap()) {
moveCameraToPosition(new GeoPoint(latLnge.getLatitude(), latLnge.getLongitude()));
}
return latLnge; return latLnge;
} }
@Override @Override
public fr.free.nrw.commons.location.LatLng getMapFocus() { public fr.free.nrw.commons.location.LatLng getMapFocus() {
fr.free.nrw.commons.location.LatLng mapFocusedLatLng = new fr.free.nrw.commons.location.LatLng( fr.free.nrw.commons.location.LatLng mapFocusedLatLng = new fr.free.nrw.commons.location.LatLng(
binding.mapView.getMapCenter().getLatitude(), binding.mapView.getMapCenter().getLongitude(), 100); binding.mapView.getMapCenter().getLatitude(),
binding.mapView.getMapCenter().getLongitude(), 100);
return mapFocusedLatLng; return mapFocusedLatLng;
} }
@ -910,9 +993,19 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
}; };
} }
@Override /**
public void onLocationPermissionDenied(String toastMessage) {} * helper function to confirm that this fragment has been attached.
**/
public boolean isAttachedToActivity() {
boolean attached = isVisible() && getActivity() != null;
return attached;
}
@Override @Override
public void onLocationPermissionGranted() {} public void onLocationPermissionDenied(String toastMessage) {
}
@Override
public void onLocationPermissionGranted() {
}
} }

View file

@ -123,10 +123,13 @@ data class LatLng(
/** /**
* Gets a URI for a Google Maps intent at the location. * Gets a URI for a Google Maps intent at the location.
*
* @paraam zoom The zoom level
* @return URI for the intent
*/ */
fun getGmmIntentUri(): Uri { fun getGmmIntentUri(zoom: Double): Uri = Uri.parse(
return Uri.parse("geo:$latitude,$longitude?z=16") "geo:$latitude,$longitude?q=$latitude,$longitude&z=${zoom}"
} )
override fun writeToParcel(parcel: Parcel, flags: Int) { override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeDouble(latitude) parcel.writeDouble(latitude)

View file

@ -430,7 +430,11 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
else -> null else -> null
} }
position?.let { Utils.handleGeoCoordinates(this, it) } position?.let {
mapView?.zoomLevelDouble?.let { zoomLevel ->
Utils.handleGeoCoordinates(this, it, zoomLevel)
} ?: Utils.handleGeoCoordinates(this, it)
}
} }
/** /**

View file

@ -650,10 +650,8 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
} }
private fun onDepictionsLoaded(idAndCaptions: List<IdAndCaptions>) { private fun onDepictionsLoaded(idAndCaptions: List<IdAndCaptions>) {
binding.depictsLayout.visibility = binding.depictsLayout.visibility = View.VISIBLE
if (idAndCaptions.isEmpty()) View.GONE else View.VISIBLE binding.depictionsEditButton.visibility = View.VISIBLE
binding.depictionsEditButton.visibility =
if (idAndCaptions.isEmpty()) View.GONE else View.VISIBLE
buildDepictionList(idAndCaptions) buildDepictionList(idAndCaptions)
} }
@ -863,8 +861,22 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
*/ */
private fun buildDepictionList(idAndCaptions: List<IdAndCaptions>) { private fun buildDepictionList(idAndCaptions: List<IdAndCaptions>) {
binding.mediaDetailDepictionContainer.removeAllViews() binding.mediaDetailDepictionContainer.removeAllViews()
// Create a mutable list from the original list
val mutableIdAndCaptions = idAndCaptions.toMutableList()
if (mutableIdAndCaptions.isEmpty()) {
// Create a placeholder IdAndCaptions object and add it to the list
mutableIdAndCaptions.add(
IdAndCaptions(
id = media?.pageId ?: "", // Use an empty string if media?.pageId is null
captions = mapOf(Locale.getDefault().language to getString(R.string.detail_panel_cats_none)) // Create a Map with the language as the key and the message as the value
)
)
}
val locale: String = Locale.getDefault().language val locale: String = Locale.getDefault().language
for (idAndCaption: IdAndCaptions in idAndCaptions) { for (idAndCaption: IdAndCaptions in mutableIdAndCaptions) {
binding.mediaDetailDepictionContainer.addView( binding.mediaDetailDepictionContainer.addView(
buildDepictLabel( buildDepictLabel(
getDepictionCaption(idAndCaption, locale), getDepictionCaption(idAndCaption, locale),
@ -875,6 +887,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
} }
} }
private fun getDepictionCaption(idAndCaption: IdAndCaptions, locale: String): String? { private fun getDepictionCaption(idAndCaption: IdAndCaptions, locale: String): String? {
// Check if the Depiction Caption is available in user's locale // Check if the Depiction Caption is available in user's locale
// if not then check for english, else show any available. // if not then check for english, else show any available.

View file

@ -233,6 +233,11 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private Place nearestPlace; private Place nearestPlace;
private volatile boolean stopQuery; private volatile boolean stopQuery;
// Explore map data (for if we came from Explore)
private double prevZoom;
private double prevLatitude;
private double prevLongitude;
private final Handler searchHandler = new Handler(); private final Handler searchHandler = new Handler();
private Runnable searchRunnable; private Runnable searchRunnable;
@ -247,27 +252,28 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult = private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(), registerForActivityResult(new StartActivityForResult(),
result -> { result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
});
}); });
});
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult = private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
registerForActivityResult(new StartActivityForResult(), registerForActivityResult(new StartActivityForResult(),
result -> { result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks); controller.onPictureReturnedFromCustomSelector(result, requireActivity(),
callbacks);
});
}); });
});
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult = private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(), registerForActivityResult(new StartActivityForResult(),
result -> { result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
}); });
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult( private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
new RequestMultiplePermissions(), new RequestMultiplePermissions(),
@ -337,12 +343,15 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override @Override
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) { final Bundle savedInstanceState) {
loadExploreMapData();
binding = FragmentNearbyParentBinding.inflate(inflater, container, false); binding = FragmentNearbyParentBinding.inflate(inflater, container, false);
view = binding.getRoot(); view = binding.getRoot();
initNetworkBroadCastReceiver(); initNetworkBroadCastReceiver();
scope = LifecycleOwnerKt.getLifecycleScope(getViewLifecycleOwner()); scope = LifecycleOwnerKt.getLifecycleScope(getViewLifecycleOwner());
presenter = new NearbyParentFragmentPresenter(bookmarkLocationDao, placesRepository, nearbyController); presenter = new NearbyParentFragmentPresenter(bookmarkLocationDao, placesRepository,
nearbyController);
progressDialog = new ProgressDialog(getActivity()); progressDialog = new ProgressDialog(getActivity());
progressDialog.setCancelable(false); progressDialog.setCancelable(false);
progressDialog.setMessage("Saving in progress..."); progressDialog.setMessage("Saving in progress...");
@ -359,6 +368,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
inflater.inflate(R.menu.nearby_fragment_menu, menu); inflater.inflate(R.menu.nearby_fragment_menu, menu);
MenuItem refreshButton = menu.findItem(R.id.item_refresh); MenuItem refreshButton = menu.findItem(R.id.item_refresh);
MenuItem listMenu = menu.findItem(R.id.list_sheet); MenuItem listMenu = menu.findItem(R.id.list_sheet);
MenuItem showInExploreButton = menu.findItem(R.id.list_item_show_in_explore);
MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx); MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx);
MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml); MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml);
refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@ -379,6 +389,17 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
return false; return false;
} }
}); });
showInExploreButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(@NonNull MenuItem item) {
((MainActivity) getContext()).loadExploreMapFromNearby(
binding.map.getZoomLevelDouble(),
binding.map.getMapCenter().getLatitude(),
binding.map.getMapCenter().getLongitude()
);
return false;
}
});
saveAsGPXButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { saveAsGPXButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override @Override
@ -467,6 +488,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
binding.map.getOverlays().add(scaleBarOverlay); binding.map.getOverlays().add(scaleBarOverlay);
binding.map.getZoomController().setVisibility(Visibility.NEVER); binding.map.getZoomController().setVisibility(Visibility.NEVER);
binding.map.getController().setZoom(ZOOM_LEVEL); binding.map.getController().setZoom(ZOOM_LEVEL);
// if we came from Explore map using 'Show in Nearby', load Explore map camera position
if (isCameFromExploreMap()) {
moveCameraToPosition(
new GeoPoint(prevLatitude, prevLongitude),
prevZoom,
1L
);
}
binding.map.getOverlays().add(mapEventsOverlay); binding.map.getOverlays().add(mapEventsOverlay);
binding.map.addMapListener(new MapListener() { binding.map.addMapListener(new MapListener() {
@ -489,11 +518,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
initNearbyFilter(); initNearbyFilter();
addCheckBoxCallback(); addCheckBoxCallback();
moveCameraToPosition(lastMapFocus); if (!isCameFromExploreMap()) {
moveCameraToPosition(lastMapFocus);
}
initRvNearbyList(); initRvNearbyList();
onResume(); onResume();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); binding.tvAttribution.setText(
Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY));
} else { } else {
//noinspection deprecation //noinspection deprecation
binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution)));
@ -545,6 +577,28 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
} }
/**
* Fetch Explore map camera data from fragment arguments if any.
*/
public void loadExploreMapData() {
// get fragment arguments
if (getArguments() != null) {
prevZoom = getArguments().getDouble("prev_zoom");
prevLatitude = getArguments().getDouble("prev_latitude");
prevLongitude = getArguments().getDouble("prev_longitude");
}
}
/**
* Checks if fragment arguments contain data from Explore map. if present, then the user
* navigated from Explore using 'Show in Nearby'.
*
* @return true if user navigated from Explore map
**/
public boolean isCameFromExploreMap() {
return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0;
}
/** /**
* Initialise background based on theme, this should be doe ideally via styles, that would need * Initialise background based on theme, this should be doe ideally via styles, that would need
* another refactor * another refactor
@ -625,7 +679,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
mapCenter = targetP; mapCenter = targetP;
binding.map.getController().setCenter(targetP); binding.map.getController().setCenter(targetP);
recenterMarkerToPosition(targetP); recenterMarkerToPosition(targetP);
moveCameraToPosition(targetP); if (!isCameFromExploreMap()) {
moveCameraToPosition(targetP);
}
} else if (locationManager.isGPSProviderEnabled() } else if (locationManager.isGPSProviderEnabled()
|| locationManager.isNetworkProviderEnabled()) { || locationManager.isNetworkProviderEnabled()) {
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER);
@ -669,7 +725,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} else { } else {
lastKnownLocation = MapUtils.getDefaultLatLng(); lastKnownLocation = MapUtils.getDefaultLatLng();
} }
if (binding.map != null) { if (binding.map != null && !isCameFromExploreMap()) {
moveCameraToPosition( moveCameraToPosition(
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
} }
@ -739,8 +795,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
/** /**
* Determines the number of spans (columns) in the RecyclerView based on device orientation * Determines the number of spans (columns) in the RecyclerView based on device orientation and
* and adapter item count. * adapter item count.
* *
* @return The number of spans to be used in the RecyclerView. * @return The number of spans to be used in the RecyclerView.
*/ */
@ -1175,7 +1231,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
/** /**
* Clears the Nearby local cache and then calls for pin details to be fetched afresh. * Clears the Nearby local cache and then calls for pin details to be fetched afresh.
*
*/ */
private void emptyCache() { private void emptyCache() {
// reload the map once the cache is cleared // reload the map once the cache is cleared
@ -1338,7 +1393,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
/** /**
* Fetches and updates the data for a specific place, then updates the corresponding marker on the map. * Fetches and updates the data for a specific place, then updates the corresponding marker on
* the map.
* *
* @param entity The entity ID of the place. * @param entity The entity ID of the place.
* @param place The Place object containing the initial place data. * @param place The Place object containing the initial place data.
@ -1469,9 +1525,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} }
/** /**
* Stops any ongoing queries and clears all disposables. * Stops any ongoing queries and clears all disposables. This method sets the stopQuery flag to
* This method sets the stopQuery flag to true and clears the compositeDisposable * true and clears the compositeDisposable to prevent any further processing.
* to prevent any further processing.
*/ */
@Override @Override
public void stopQuery() { public void stopQuery() {
@ -1624,7 +1679,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
new Builder(getContext()) new Builder(getContext())
.setMessage(R.string.login_alert_message) .setMessage(R.string.login_alert_message)
.setCancelable(false) .setCancelable(false)
.setNegativeButton(R.string.cancel, (dialog, which) -> {}) .setNegativeButton(R.string.cancel, (dialog, which) -> {
})
.setPositiveButton(R.string.login, (dialog, which) -> { .setPositiveButton(R.string.login, (dialog, which) -> {
// logout of the app // logout of the app
BaseLogoutListener logoutListener = new BaseLogoutListener(getActivity()); BaseLogoutListener logoutListener = new BaseLogoutListener(getActivity());
@ -1743,7 +1799,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
final boolean filterForPlaceState, final boolean filterForPlaceState,
final boolean filterForAllNoneType) { final boolean filterForAllNoneType) {
final boolean displayExists = false; final boolean displayExists = false;
final boolean displayNeedsPhoto= false; final boolean displayNeedsPhoto = false;
final boolean displayWlm = false; final boolean displayWlm = false;
if (selectedLabels == null || selectedLabels.size() == 0) { if (selectedLabels == null || selectedLabels.size() == 0) {
replaceMarkerOverlays(NearbyController.markerLabelList); replaceMarkerOverlays(NearbyController.markerLabelList);
@ -1903,8 +1959,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
/** /**
* Adds multiple markers representing places to the map and handles item gestures. * Adds multiple markers representing places to the map and handles item gestures.
* *
* @param markerPlaceGroups The list of marker place groups containing the places and * @param markerPlaceGroups The list of marker place groups containing the places and their
* their bookmarked status * bookmarked status
*/ */
@Override @Override
public void replaceMarkerOverlays(final List<MarkerPlaceGroup> markerPlaceGroups) { public void replaceMarkerOverlays(final List<MarkerPlaceGroup> markerPlaceGroups) {
@ -1913,7 +1969,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
for (int i = markerPlaceGroups.size() - 1; i >= 0; i--) { for (int i = markerPlaceGroups.size() - 1; i >= 0; i--) {
newMarkers.add( newMarkers.add(
convertToMarker(markerPlaceGroups.get(i).getPlace(), convertToMarker(markerPlaceGroups.get(i).getPlace(),
markerPlaceGroups.get(i).getIsBookmarked()) markerPlaceGroups.get(i).getIsBookmarked())
); );
} }
clearAllMarkers(); clearAllMarkers();
@ -2103,7 +2159,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (binding.fabCamera.isShown()) { if (binding.fabCamera.isShown()) {
Timber.d("Camera button tapped. Place: %s", selectedPlace.toString()); Timber.d("Camera button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace); storeSharedPrefs(selectedPlace);
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher,
cameraPickLauncherForResult);
} }
}); });
@ -2121,7 +2178,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (binding.fabCustomGallery.isShown()) { if (binding.fabCustomGallery.isShown()) {
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString()); Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace); storeSharedPrefs(selectedPlace);
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult); controller.initiateCustomGalleryPickWithPermission(getActivity(),
customSelectorLauncherForResult);
} }
}); });
} }
@ -2296,6 +2354,18 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
binding.map.getController().animateTo(geoPoint); binding.map.getController().animateTo(geoPoint);
} }
/**
* Moves the camera of the map view to the specified GeoPoint at specified zoom level and speed
* using an animation.
*
* @param geoPoint The GeoPoint representing the new camera position for the map.
* @param zoom Zoom level of the map camera
* @param speed Speed of animation
*/
private void moveCameraToPosition(GeoPoint geoPoint, double zoom, long speed) {
binding.map.getController().animateTo(geoPoint, zoom, speed);
}
@Override @Override
public void onBottomSheetItemClick(@Nullable View view, int position) { public void onBottomSheetItemClick(@Nullable View view, int position) {
BottomSheetItem item = dataList.get(position); BottomSheetItem item = dataList.get(position);
@ -2309,7 +2379,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
updateBookmarkButtonImage(selectedPlace); updateBookmarkButtonImage(selectedPlace);
break; break;
case R.drawable.ic_directions_black_24dp: case R.drawable.ic_directions_black_24dp:
Utils.handleGeoCoordinates(this.getContext(), selectedPlace.getLocation()); Utils.handleGeoCoordinates(this.getContext(), selectedPlace.getLocation(),
binding.map.getZoomLevelDouble());
break; break;
case R.drawable.ic_wikidata_logo_24dp: case R.drawable.ic_wikidata_logo_24dp:
Utils.handleWebUrl(this.getContext(), selectedPlace.siteLinks.getWikidataLink()); Utils.handleWebUrl(this.getContext(), selectedPlace.siteLinks.getWikidataLink());

View file

@ -14,11 +14,15 @@ class QuizController {
private val quiz: ArrayList<QuizQuestion> = ArrayList() private val quiz: ArrayList<QuizQuestion> = ArrayList()
private val URL_FOR_SELFIE = "https://i.imgur.com/0fMYcpM.jpg" companion object{
private val URL_FOR_TAJ_MAHAL = "https://upload.wikimedia.org/wikipedia/commons/1/15/Taj_Mahal-03.jpg"
private val URL_FOR_BLURRY_IMAGE = "https://i.imgur.com/Kepb5jR.jpg" const val URL_FOR_SELFIE = "https://i.imgur.com/0fMYcpM.jpg"
private val URL_FOR_SCREENSHOT = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Social_media_app_mockup_screenshot.svg/500px-Social_media_app_mockup_screenshot.svg.png" const val URL_FOR_TAJ_MAHAL = "https://upload.wikimedia.org/wikipedia/commons/1/15/Taj_Mahal-03.jpg"
private val URL_FOR_EVENT = "https://upload.wikimedia.org/wikipedia/commons/5/51/HouseBuildingInNorthernVietnam.jpg" const val URL_FOR_BLURRY_IMAGE = "https://i.imgur.com/Kepb5jR.jpg"
const val URL_FOR_SCREENSHOT = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Social_media_app_mockup_screenshot.svg/500px-Social_media_app_mockup_screenshot.svg.png"
const val URL_FOR_EVENT = "https://upload.wikimedia.org/wikipedia/commons/5/51/HouseBuildingInNorthernVietnam.jpg"
}
fun initialize(context: Context) { fun initialize(context: Context) {
val q1 = QuizQuestion( val q1 = QuizQuestion(

View file

@ -45,12 +45,12 @@ class ReviewActivity : BaseActivity() {
private var hasNonHiddenCategories = false private var hasNonHiddenCategories = false
var media: Media? = null var media: Media? = null
private val SAVED_MEDIA = "saved_media" private val savedMedia = "saved_media"
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
media?.let { media?.let {
outState.putParcelable(SAVED_MEDIA, it) outState.putParcelable(savedMedia, it)
} }
} }
@ -90,8 +90,8 @@ class ReviewActivity : BaseActivity() {
PorterDuff.Mode.SRC_IN PorterDuff.Mode.SRC_IN
) )
if (savedInstanceState?.getParcelable<Media>(SAVED_MEDIA) != null) { if (savedInstanceState?.getParcelable<Media>(savedMedia) != null) {
updateImage(savedInstanceState.getParcelable(SAVED_MEDIA)!!) updateImage(savedInstanceState.getParcelable(savedMedia)!!)
setUpMediaDetailOnOrientation() setUpMediaDetailOnOrientation()
} else { } else {
runRandomizer() runRandomizer()
@ -188,7 +188,7 @@ class ReviewActivity : BaseActivity() {
return return
} }
binding.reviewImageView.setImageURI(media.imageUrl) binding.reviewImageView.setImageURI(media.thumbUrl)
reviewController.onImageRefreshed(media) // filename is updated reviewController.onImageRefreshed(media) // filename is updated
compositeDisposable.add( compositeDisposable.add(

View file

@ -31,7 +31,7 @@ class ReviewImageFragment : CommonsDaggerSupportFragment() {
lateinit var sessionManager: SessionManager lateinit var sessionManager: SessionManager
// Constant variable used to store user's key name for onSaveInstanceState method // Constant variable used to store user's key name for onSaveInstanceState method
private val SAVED_USER = "saved_user" private val savedUser = "saved_user"
// Variable that stores the value of user // Variable that stores the value of user
private var user: String? = null private var user: String? = null
@ -129,7 +129,7 @@ class ReviewImageFragment : CommonsDaggerSupportFragment() {
question = getString(R.string.review_thanks) question = getString(R.string.review_thanks)
user = reviewActivity.reviewController.firstRevision?.user() user = reviewActivity.reviewController.firstRevision?.user()
?: savedInstanceState?.getString(SAVED_USER) ?: savedInstanceState?.getString(savedUser)
//if the user is null because of whatsoever reason, review will not be sent anyways //if the user is null because of whatsoever reason, review will not be sent anyways
if (!user.isNullOrEmpty()) { if (!user.isNullOrEmpty()) {
@ -172,7 +172,7 @@ class ReviewImageFragment : CommonsDaggerSupportFragment() {
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
//Save user name when configuration changes happen //Save user name when configuration changes happen
outState.putString(SAVED_USER, user) outState.putString(savedUser, user)
} }
private val reviewCallback: ReviewController.ReviewCallback private val reviewCallback: ReviewController.ReviewCallback

View file

@ -33,6 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener 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.R
import fr.free.nrw.commons.Utils import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.activity.SingleWebViewActivity import fr.free.nrw.commons.activity.SingleWebViewActivity
@ -85,7 +86,6 @@ class SettingsFragment : PreferenceFragmentCompat() {
private var languageHistoryListView: ListView? = null private var languageHistoryListView: ListView? = null
private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>> private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>
private val GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content"
private val cameraPickLauncherForResult: ActivityResultLauncher<Intent> = private val cameraPickLauncherForResult: ActivityResultLauncher<Intent> =
registerForActivityResult(StartActivityForResult()) { result -> registerForActivityResult(StartActivityForResult()) { result ->
@ -271,6 +271,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
findPreference<Preference>("managed_exif_tags")?.isEnabled = false findPreference<Preference>("managed_exif_tags")?.isEnabled = false
findPreference<Preference>("openDocumentPhotoPickerPref")?.isEnabled = false findPreference<Preference>("openDocumentPhotoPickerPref")?.isEnabled = false
findPreference<Preference>("inAppCameraLocationPref")?.isEnabled = false findPreference<Preference>("inAppCameraLocationPref")?.isEnabled = false
findPreference<Preference>("vanishAccount")?.isEnabled = false
} }
} }
@ -511,6 +512,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
@Suppress("LongLine") @Suppress("LongLine")
companion object { companion object {
const val GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content"
private const val VANISH_ACCOUNT_URL = "https://meta.m.wikimedia.org/wiki/Special:Contact/accountvanishapps" private const val VANISH_ACCOUNT_URL = "https://meta.m.wikimedia.org/wiki/Special:Contact/accountvanishapps"
private const val VANISH_ACCOUNT_SUCCESS_URL = "https://meta.m.wikimedia.org/wiki/Special:GlobalVanishRequest/vanished" private const val VANISH_ACCOUNT_SUCCESS_URL = "https://meta.m.wikimedia.org/wiki/Special:GlobalVanishRequest/vanished"
/** /**

View file

@ -10,7 +10,6 @@ import fr.free.nrw.commons.utils.ImageUtils.IMAGE_KEEP
import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK
import fr.free.nrw.commons.utils.ImageUtilsWrapper import fr.free.nrw.commons.utils.ImageUtilsWrapper
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.functions.Function
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import timber.log.Timber import timber.log.Timber
@ -26,7 +25,7 @@ class ImageProcessingService @Inject constructor(
private val fileUtilsWrapper: FileUtilsWrapper, private val fileUtilsWrapper: FileUtilsWrapper,
private val imageUtilsWrapper: ImageUtilsWrapper, private val imageUtilsWrapper: ImageUtilsWrapper,
private val readFBMD: ReadFBMD, private val readFBMD: ReadFBMD,
private val EXIFReader: EXIFReader, private val exifReader: EXIFReader,
private val mediaClient: MediaClient private val mediaClient: MediaClient
) { ) {
/** /**
@ -94,7 +93,7 @@ class ImageProcessingService @Inject constructor(
* the presence of some basic Exif metadata. * the presence of some basic Exif metadata.
*/ */
private fun checkEXIF(filepath: String): Single<Int> = private fun checkEXIF(filepath: String): Single<Int> =
EXIFReader.processMetadata(filepath) exifReader.processMetadata(filepath)
/** /**

View file

@ -16,6 +16,9 @@ import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener import com.karumi.dexter.listener.multi.MultiplePermissionsListener
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
object PermissionUtils { object PermissionUtils {
@ -130,7 +133,7 @@ object PermissionUtils {
vararg permissions: String vararg permissions: String
) { ) {
if (hasPartialAccess(activity)) { if (hasPartialAccess(activity)) {
Thread(onPermissionGranted).start() CoroutineScope(Dispatchers.Main).launch { onPermissionGranted.run() }
return return
} }
checkPermissionsAndPerformAction( checkPermissionsAndPerformAction(
@ -166,13 +169,15 @@ object PermissionUtils {
rationaleMessage: Int, rationaleMessage: Int,
vararg permissions: String vararg permissions: String
) { ) {
val scope = CoroutineScope(Dispatchers.Main)
Dexter.withActivity(activity) Dexter.withActivity(activity)
.withPermissions(*permissions) .withPermissions(*permissions)
.withListener(object : MultiplePermissionsListener { .withListener(object : MultiplePermissionsListener {
override fun onPermissionsChecked(report: MultiplePermissionsReport) { override fun onPermissionsChecked(report: MultiplePermissionsReport) {
when { when {
report.areAllPermissionsGranted() || hasPartialAccess(activity) -> report.areAllPermissionsGranted() || hasPartialAccess(activity) ->
Thread(onPermissionGranted).start() scope.launch { onPermissionGranted.run() }
report.isAnyPermissionPermanentlyDenied -> { report.isAnyPermissionPermanentlyDenied -> {
DialogUtil.showAlertDialog( DialogUtil.showAlertDialog(
activity, activity,
@ -189,7 +194,7 @@ object PermissionUtils {
null, null null, null
) )
} }
else -> Thread(onPermissionDenied).start() else -> scope.launch { onPermissionDenied?.run() }
} }
} }

View file

@ -21,9 +21,14 @@ abstract class SwipableCardView @JvmOverloads constructor(
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : CardView(context, attrs, defStyleAttr) { ) : CardView(context, attrs, defStyleAttr) {
companion object{
const val MINIMUM_THRESHOLD_FOR_SWIPE = 100f
}
private var x1 = 0f private var x1 = 0f
private var x2 = 0f private var x2 = 0f
private val MINIMUM_THRESHOLD_FOR_SWIPE = 100f
init { init {
interceptOnTouchListener() interceptOnTouchListener()

View file

@ -49,6 +49,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/all_images_uploaded_or_marked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:textSize="16sp"
android:padding="@dimen/standard_gap"
android:textColor="@color/text_color_selector"
android:text="@string/congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
/>
<ProgressBar <ProgressBar
android:id="@+id/loader" android:id="@+id/loader"

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<item
android:id="@+id/action_search"
android:title="@string/menu_search_button"
android:icon="?attr/search_icon"
android:orderInCategory="1"
app:showAsAction="ifRoom"
/>
<item android:id="@+id/list_item_show_in_nearby"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:title="@string/show_in_nearby"
android:visible="false"
/>
</menu>

View file

@ -12,6 +12,12 @@
android:icon="@drawable/ic_list_white_24dp" android:icon="@drawable/ic_list_white_24dp"
/> />
<item android:id="@+id/list_item_show_in_explore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:title="@string/show_in_explore"
/>
<item android:id="@+id/list_item_gpx" <item android:id="@+id/list_item_gpx"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -5,6 +5,7 @@
* Asma * Asma
* Azouz.anis * Azouz.anis
* ButterflyOfFire * ButterflyOfFire
* Cigaryno
* Claw eg * Claw eg
* Dr-Taher * Dr-Taher
* Dr. Mohammed * Dr. Mohammed
@ -20,6 +21,7 @@
* NancyMilad * NancyMilad
* OsamaK * OsamaK
* Tala Ali * Tala Ali
* XIDME
* أيوب * أيوب
* أَحمد * أَحمد
* ترجمان05 * ترجمان05
@ -408,7 +410,7 @@
<string name="error_fetching_nearby_monuments">خطأ في جلب المعالم القريبة.</string> <string name="error_fetching_nearby_monuments">خطأ في جلب المعالم القريبة.</string>
<string name="no_recent_searches">لا توجد عمليات بحث حديثة</string> <string name="no_recent_searches">لا توجد عمليات بحث حديثة</string>
<string name="delete_recent_searches_dialog">هل أنت متأكد من أنك تريد مسح سجل بحثك؟</string> <string name="delete_recent_searches_dialog">هل أنت متأكد من أنك تريد مسح سجل بحثك؟</string>
<string name="cancel_upload_dialog">هل انت متأكد انك تريد الغاء هذا التحميل</string> <string name="cancel_upload_dialog">هل أنت متأكد أنك تريد إلغاء هذا التحميل؟</string>
<string name="delete_search_dialog">هل تريد حذف هذا البحث؟</string> <string name="delete_search_dialog">هل تريد حذف هذا البحث؟</string>
<string name="search_history_deleted">تم حذف سجل البحث</string> <string name="search_history_deleted">تم حذف سجل البحث</string>
<string name="nominate_delete">ترشيح للحذف</string> <string name="nominate_delete">ترشيح للحذف</string>
@ -882,4 +884,7 @@
<string name="account_vanish_request_confirm">الاختفاء هو &lt;b&gt;الملاذ الأخير&lt;/b&gt; ويجب &lt;b&gt;استخدامه فقط عندما ترغب في التوقف عن التحرير إلى الأبد&lt;/b&gt; وأيضًا لإخفاء أكبر عدد ممكن من ارتباطاتك السابقة.&lt;br/&gt;&lt;br/&gt; يتم حذف الحساب على ويكيميديا كومنز عن طريق تغيير اسم حسابك بحيث لا يتمكن الآخرون من التعرف على مساهماتك في عملية تسمى اختفاء الحساب. &lt;b&gt;لا يضمن الاختفاء عدم الكشف عن الهوية تمامًا أو إزالة المساهمات في المشاريع&lt;/b&gt; .</string> <string name="account_vanish_request_confirm">الاختفاء هو &lt;b&gt;الملاذ الأخير&lt;/b&gt; ويجب &lt;b&gt;استخدامه فقط عندما ترغب في التوقف عن التحرير إلى الأبد&lt;/b&gt; وأيضًا لإخفاء أكبر عدد ممكن من ارتباطاتك السابقة.&lt;br/&gt;&lt;br/&gt; يتم حذف الحساب على ويكيميديا كومنز عن طريق تغيير اسم حسابك بحيث لا يتمكن الآخرون من التعرف على مساهماتك في عملية تسمى اختفاء الحساب. &lt;b&gt;لا يضمن الاختفاء عدم الكشف عن الهوية تمامًا أو إزالة المساهمات في المشاريع&lt;/b&gt; .</string>
<string name="caption">الشرح</string> <string name="caption">الشرح</string>
<string name="caption_copied_to_clipboard">تم نسخ التسمية التوضيحية إلى الحافظة</string> <string name="caption_copied_to_clipboard">تم نسخ التسمية التوضيحية إلى الحافظة</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">مبروك، جميع الصور الموجودة في هذا الألبوم تم تحميلها أو تم وضع علامة عليها بأنها غير قابلة للتحميل.</string>
<string name="show_in_explore">عرض في استكشاف</string>
<string name="show_in_nearby">عرض في المناطق القريبة</string>
</resources> </resources>

View file

@ -820,4 +820,7 @@
<string name="account_vanish_request_confirm">Forsvinding er en &lt;b&gt;sidste udvej&lt;/b&gt; og bør &lt;b&gt;kun bruges, når du for altid ønsker at stoppe med at redigere&lt;/b&gt; og også for at skjule så mange af dine tidligere tilknytninger som muligt.&lt;br/&gt;&lt;br/&gt; Kontosletning på Wikipedia Commons sker ved at ændre dit kontonavn, således at andre ikke kan genkende dine bidrag i en proces, der kaldes kontoforsvinding (Vanishing). &lt;b&gt;Forsvinding garanterer ikke fuldstændig anonymitet eller fjerner bidrag til projekterne&lt;/b&gt; .</string> <string name="account_vanish_request_confirm">Forsvinding er en &lt;b&gt;sidste udvej&lt;/b&gt; og bør &lt;b&gt;kun bruges, når du for altid ønsker at stoppe med at redigere&lt;/b&gt; og også for at skjule så mange af dine tidligere tilknytninger som muligt.&lt;br/&gt;&lt;br/&gt; Kontosletning på Wikipedia Commons sker ved at ændre dit kontonavn, således at andre ikke kan genkende dine bidrag i en proces, der kaldes kontoforsvinding (Vanishing). &lt;b&gt;Forsvinding garanterer ikke fuldstændig anonymitet eller fjerner bidrag til projekterne&lt;/b&gt; .</string>
<string name="caption">Billedtekst</string> <string name="caption">Billedtekst</string>
<string name="caption_copied_to_clipboard">Billedtekst kopieret til udklipsholder</string> <string name="caption_copied_to_clipboard">Billedtekst kopieret til udklipsholder</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Tillykke, alle billeder i dette album er enten blevet uploadet eller markeret som ikke til upload.</string>
<string name="show_in_explore">Vis i Udforsk</string>
<string name="show_in_nearby">Vis i I nærheden</string>
</resources> </resources>

View file

@ -349,4 +349,6 @@
<string name="read_help_link">Tayêna bıwane</string> <string name="read_help_link">Tayêna bıwane</string>
<string name="description">Şınasnayış</string> <string name="description">Şınasnayış</string>
<string name="reset">Raçarne</string> <string name="reset">Raçarne</string>
<string name="account">Hesab</string>
<string name="caption">Bınnuşte</string>
</resources> </resources>

View file

@ -855,4 +855,6 @@
<string name="file_usages_container_heading">Utilisations du fichier</string> <string name="file_usages_container_heading">Utilisations du fichier</string>
<string name="caption">Légende</string> <string name="caption">Légende</string>
<string name="caption_copied_to_clipboard">Légende copiée dans le presse-papier</string> <string name="caption_copied_to_clipboard">Légende copiée dans le presse-papier</string>
<string name="show_in_explore">Afficher dans Explorer</string>
<string name="show_in_nearby">Afficher à proximité</string>
</resources> </resources>

View file

@ -79,7 +79,7 @@
<string name="retry_limit_reached">Vu atingis la maxim granda quanto di probi por sendar arkivo permisata! Voluntez interuptar la kargajo, e probez itere!</string> <string name="retry_limit_reached">Vu atingis la maxim granda quanto di probi por sendar arkivo permisata! Voluntez interuptar la kargajo, e probez itere!</string>
<string name="unrestricted_battery_mode">Ka desmuntar l\'optimizo di la baterio?</string> <string name="unrestricted_battery_mode">Ka desmuntar l\'optimizo di la baterio?</string>
<string name="suggest_unrestricted_mode">Sendar plu kam 3 imaji esas plu efikiva kande l\'optimizo di la baterio esas desmuntita. Voluntez desmuntar l\'optimizo di la baterio del ajusti dil utensilo de Commons, por plugrandigar l\'efikeso. \n\nQuale desmuntar l\'optimizo-sistemo di la baterio:\n\n#: Kliktez la butono \"ajusti\" (\'\'Settings\'\') adinfre.\n\n#: Selektez \"ne optimizita (\'\'Not optimized\'\') por omna utensili (\'\'All apps\'\').\n\n#: Serchez \"Commons\" o \"fr.free.nrw.commons\".\n\n#: Kliktez ol e selektez \"ne optimizar\" (\'\'Don\'t optimize\'\').\n\n#: Kliktez \"facita\" (\'\'Done\'\').</string> <string name="suggest_unrestricted_mode">Sendar plu kam 3 imaji esas plu efikiva kande l\'optimizo di la baterio esas desmuntita. Voluntez desmuntar l\'optimizo di la baterio del ajusti dil utensilo de Commons, por plugrandigar l\'efikeso. \n\nQuale desmuntar l\'optimizo-sistemo di la baterio:\n\n#: Kliktez la butono \"ajusti\" (\'\'Settings\'\') adinfre.\n\n#: Selektez \"ne optimizita (\'\'Not optimized\'\') por omna utensili (\'\'All apps\'\').\n\n#: Serchez \"Commons\" o \"fr.free.nrw.commons\".\n\n#: Kliktez ol e selektez \"ne optimizar\" (\'\'Don\'t optimize\'\').\n\n#: Kliktez \"facita\" (\'\'Done\'\').</string>
<string name="authentication_failed" fuzzy="true">L\'autentikigo faliis, voluntez itere enirar.</string> <string name="authentication_failed">Autentikigo faliis. Voluntez itere enirar.</string>
<string name="uploading_started">Komencis sendar!</string> <string name="uploading_started">Komencis sendar!</string>
<string name="uploading_queued">Sendajo ajornata (modulo \"limitizita konekto\" aktiva)</string> <string name="uploading_queued">Sendajo ajornata (modulo \"limitizita konekto\" aktiva)</string>
<string name="upload_completed_notification_title">%1$s sendesis!</string> <string name="upload_completed_notification_title">%1$s sendesis!</string>
@ -100,17 +100,19 @@
<string name="menu_from_camera">Fotografar</string> <string name="menu_from_camera">Fotografar</string>
<string name="menu_nearby">Proxime</string> <string name="menu_nearby">Proxime</string>
<string name="provider_contributions">Mea sendaji</string> <string name="provider_contributions">Mea sendaji</string>
<string name="menu_copy_link">Kopiez ligilo</string>
<string name="menu_link_copied">La ligilo kopiesis a \'\'clipboard\'\'.</string>
<string name="menu_share">Partigar</string> <string name="menu_share">Partigar</string>
<string name="menu_view_file_page">Vidar arkivo-pagino</string> <string name="menu_view_file_page">Vidar arkivo-pagino</string>
<string name="share_title_hint">Titulo (Bezonata)</string> <string name="share_title_hint">Titulo (Bezonata)</string>
<string name="add_caption_toast">Voluntez informar deskripto-texto por ca arkivo</string> <string name="add_caption_toast">Voluntez informar deskripto-texto por ca arkivo</string>
<string name="share_description_hint">Deskripto</string> <string name="share_description_hint">Deskripto</string>
<string name="share_caption_hint">Deskripto-texto</string> <string name="share_caption_hint">Deskripto-texto</string>
<string name="login_failed_network" fuzzy="true">Ne esis posibla facar \'\'log - in\'\' - la reto faliis</string> <string name="login_failed_network">Ne esas posibla enirar - la reto faliis</string>
<string name="login_failed_throttled">Multa sensucesa probi pri konektar. Voluntez probar itere pos kelka minuti.</string> <string name="login_failed_throttled">Multa sensucesa probi pri konektar. Voluntez probar itere pos kelka minuti.</string>
<string name="login_failed_blocked">Pardonez, ca uzero blokusesis che Commons</string> <string name="login_failed_blocked">Pardonez, ca uzero blokusesis che Commons</string>
<string name="login_failed_2fa_needed">Vu mustas uzar vua autentikigo en du etapi.</string> <string name="login_failed_2fa_needed">Vu mustas uzar vua autentikigo en du etapi.</string>
<string name="login_failed_generic" fuzzy="true">Eniro faliis</string> <string name="login_failed_generic">Eniro faliis</string>
<string name="share_upload_button">Kargar</string> <string name="share_upload_button">Kargar</string>
<string name="multiple_share_base_title">Nomizes ca ajusto</string> <string name="multiple_share_base_title">Nomizes ca ajusto</string>
<string name="provider_modifications">Modifikuri</string> <string name="provider_modifications">Modifikuri</string>
@ -118,6 +120,7 @@
<string name="categories_search_text_hint">Serchar kategorii</string> <string name="categories_search_text_hint">Serchar kategorii</string>
<string name="depicts_search_text_hint">Serchez kozi quin vua \'\'media\'\' montras (monti, \'\'Taj Mahal\'\', edc.)</string> <string name="depicts_search_text_hint">Serchez kozi quin vua \'\'media\'\' montras (monti, \'\'Taj Mahal\'\', edc.)</string>
<string name="menu_save_categories">Registragar</string> <string name="menu_save_categories">Registragar</string>
<string name="menu_overflow_desc">Menuo pri exterfluajo</string>
<string name="refresh_button">Rinovigar</string> <string name="refresh_button">Rinovigar</string>
<string name="display_list_button">Listar</string> <string name="display_list_button">Listar</string>
<string name="contributions_subtitle_zero">(Nula arkivo sendita til nun)</string> <string name="contributions_subtitle_zero">(Nula arkivo sendita til nun)</string>
@ -258,6 +261,7 @@
<string name="use_external_storage_summary">Konservar en vua enmagaziniguro la fotografuri obtenita uzanta fotografilo del utensilo (\'\'app\'\')</string> <string name="use_external_storage_summary">Konservar en vua enmagaziniguro la fotografuri obtenita uzanta fotografilo del utensilo (\'\'app\'\')</string>
<string name="login_to_your_account">Enirez en vua konto</string> <string name="login_to_your_account">Enirez en vua konto</string>
<string name="send_log_file">Sendez arkivo \'\'log\'\'</string> <string name="send_log_file">Sendez arkivo \'\'log\'\'</string>
<string name="send_log_file_description">Sendez protokolo per e-posto a developeri, por helpar la solvo di problemi dil \'\'app\'\'. Atencez: protokoli povas kontenar informi por identifiko</string>
<string name="no_web_browser">Nula retnavigilo trovita, por apertar la URL</string> <string name="no_web_browser">Nula retnavigilo trovita, por apertar la URL</string>
<string name="null_url">Eroro! URL ne trovita</string> <string name="null_url">Eroro! URL ne trovita</string>
<string name="nominate_deletion">Propozar efaco</string> <string name="nominate_deletion">Propozar efaco</string>
@ -266,11 +270,12 @@
<string name="skip_login">Saltar</string> <string name="skip_login">Saltar</string>
<string name="navigation_item_login">Enirar</string> <string name="navigation_item_login">Enirar</string>
<string name="skip_login_title">Ka vu deziras ne enirar?</string> <string name="skip_login_title">Ka vu deziras ne enirar?</string>
<string name="skip_login_message" fuzzy="true">Vu mustus facar \'\'log in\'\' por sendor imaji future.</string> <string name="skip_login_message">Future, vu mustus facar \'\'log in\'\' por sendar imaji.</string>
<string name="login_alert_message">Voluntez enirar por uzar ca utensilo</string> <string name="login_alert_message">Voluntez enirar por uzar ca utensilo</string>
<string name="copy_wikicode">Kopiez Wiki-texto a \'clipboard\'</string> <string name="copy_wikicode">Kopiez Wiki-texto a \'clipboard\'</string>
<string name="wikicode_copied">Wiki-texto kopiesis a \'clipboard\'</string> <string name="wikicode_copied">Wiki-texto kopiesis a \'clipboard\'</string>
<string name="nearby_location_not_available">Proximeso povas ne funcionar korekte, nam Lokizo ne esas disponebla.</string> <string name="nearby_location_not_available">Proximeso povas ne funcionar korekte, nam Lokizo ne esas disponebla.</string>
<string name="nearby_showing_pins_offline">Interreto nedisponebla. Montranta nur elementi enmagazinigita lokale.</string>
<string name="upload_location_access_denied">Aceso a lokizo ne permisita. Voluntez informar manuale vua lokizo por uzar ca resurso*.</string> <string name="upload_location_access_denied">Aceso a lokizo ne permisita. Voluntez informar manuale vua lokizo por uzar ca resurso*.</string>
<string name="location_permission_rationale_nearby">Permiso bezonata por montrar listo pri vicina loki</string> <string name="location_permission_rationale_nearby">Permiso bezonata por montrar listo pri vicina loki</string>
<string name="location_permission_rationale_explore">Permiso bezonata por montrar listo pri vicina imaji</string> <string name="location_permission_rationale_explore">Permiso bezonata por montrar listo pri vicina imaji</string>
@ -354,18 +359,22 @@
<string name="delete">Efacar</string> <string name="delete">Efacar</string>
<string name="Achievements">Sucesi</string> <string name="Achievements">Sucesi</string>
<string name="Profile">Profilo</string> <string name="Profile">Profilo</string>
<string name="badges">Insigni</string>
<string name="statistics">Statistiko</string> <string name="statistics">Statistiko</string>
<string name="statistics_thanks">Danki recevita</string> <string name="statistics_thanks">Danki recevita</string>
<string name="statistics_featured">Remarkinda imaji</string> <string name="statistics_featured">Remarkinda imaji</string>
<string name="statistics_wikidata_edits">Imaji tra \"Loki Vicina\"</string> <string name="statistics_wikidata_edits">Imaji tra \"Loki Vicina\"</string>
<string name="level" fuzzy="true">Nivelo</string> <string name="level">Nivelo %d</string>
<string name="profileLevel">%s (Nivelo %s)</string>
<string name="images_uploaded">Imaji sendita</string> <string name="images_uploaded">Imaji sendita</string>
<string name="image_reverts">Imaji ne reversionita</string> <string name="image_reverts">Imaji ne reversionita</string>
<string name="images_used_by_wiki">Imaji uzita</string> <string name="images_used_by_wiki">Imaji uzita</string>
<string name="achievements_share_message">Partigez vua sucesi kun vua amiki!</string> <string name="achievements_share_message">Partigez vua sucesi kun vua amiki!</string>
<string name="achievements_info_message">Vua nivelo augmentas kande vu atingas bezonata postuli. Elementi en la segmento \"statistiko\" ne augmentas vua nivelo.</string>
<string name="achievements_revert_limit_message">minima quanto bezonata:</string> <string name="achievements_revert_limit_message">minima quanto bezonata:</string>
<string name="images_uploaded_explanation">Quanto di imaji quin vu sendis a Commons, uzanta irga softwaro* por sendar li</string> <string name="images_uploaded_explanation">Quanto di imaji quin vu sendis a Commons, uzanta irga softwaro* por sendar li</string>
<string name="images_reverted_explanation">La procento di imaji quin vu sendis a Commons, qui ne efacesis pose</string> <string name="images_reverted_explanation">La procento di imaji quin vu sendis a Commons, qui ne efacesis pose</string>
<string name="images_used_explanation">La quanto di imaji sendita da vu a Commons, qui uzesis en artikli de Wikimedia.</string>
<string name="error_occurred">Eventis eroro!</string> <string name="error_occurred">Eventis eroro!</string>
<string name="notifications_channel_name_all">Avizo de Commons</string> <string name="notifications_channel_name_all">Avizo de Commons</string>
<string name="preference_author_name_toggle">Uzar personalizita autoro-nomo</string> <string name="preference_author_name_toggle">Uzar personalizita autoro-nomo</string>
@ -375,6 +384,7 @@
<string name="nearby_fragment">Vicina</string> <string name="nearby_fragment">Vicina</string>
<string name="notifications">Avizi</string> <string name="notifications">Avizi</string>
<string name="read_notifications">Avizi (lektita)</string> <string name="read_notifications">Avizi (lektita)</string>
<string name="display_nearby_notification">Montrez proxima avizo</string>
<string name="list_sheet">Listo</string> <string name="list_sheet">Listo</string>
<string name="storage_permission">Permiso pri enmagazinigo</string> <string name="storage_permission">Permiso pri enmagazinigo</string>
<string name="step_count">Etapo %1$d de %2$d: %3$s</string> <string name="step_count">Etapo %1$d de %2$d: %3$s</string>
@ -383,6 +393,8 @@
<string name="upload_title_duplicate">Arkivo kun la nomo %1$s ja existas. Ka vu deziras durigar?\n\nNoto: Sufixo adequata adjuntesos automatale a la nomo dil imajo.</string> <string name="upload_title_duplicate">Arkivo kun la nomo %1$s ja existas. Ka vu deziras durigar?\n\nNoto: Sufixo adequata adjuntesos automatale a la nomo dil imajo.</string>
<string name="title_page_bookmarks_pictures">Imaji</string> <string name="title_page_bookmarks_pictures">Imaji</string>
<string name="title_page_bookmarks_locations">Loki</string> <string name="title_page_bookmarks_locations">Loki</string>
<string name="title_page_bookmarks_categories">Kategorii</string>
<string name="menu_bookmark">Adjuntez/Removez marko-rubandi (\'\'bookmark\'\'-i)</string>
<string name="provider_bookmarks">Marko-rubandi</string> <string name="provider_bookmarks">Marko-rubandi</string>
<string name="bookmark_empty">Vu ne adjuntis marko-rubandi</string> <string name="bookmark_empty">Vu ne adjuntis marko-rubandi</string>
<string name="provider_bookmarks_location">Marko-rubandi</string> <string name="provider_bookmarks_location">Marko-rubandi</string>
@ -392,7 +404,11 @@
<string name="deletion_reason_bad_for_my_privacy">Me konstatis ke ol esas mala por mea privateso</string> <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_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="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="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">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>
<string name="no_depictions_selected">Nula deskripturo selektita</string>
<string name="back_button_warning">Cesar kargajo</string> <string name="back_button_warning">Cesar kargajo</string>
<string name="back_button_continue">Durar kargajo</string> <string name="back_button_continue">Durar kargajo</string>
<string name="search_this_area">Serchez ca areo</string> <string name="search_this_area">Serchez ca areo</string>
@ -401,15 +417,38 @@
<string name="never_ask_again">Ne pluse demandez to</string> <string name="never_ask_again">Ne pluse demandez to</string>
<string name="display_location_permission_title">Demandar lokala permiso</string> <string name="display_location_permission_title">Demandar lokala permiso</string>
<string name="display_location_permission_explanation">Demandez lokala permiso, kande bezonata por uzar karto montranta proximeso.</string> <string name="display_location_permission_explanation">Demandez lokala permiso, kande bezonata por uzar karto montranta proximeso.</string>
<string name="ends_on">Finas la:</string>
<string name="display_campaigns">Montrez kampanii</string>
<string name="display_campaigns_explanation">Videz la kampanii duranta</string>
<string name="option_allow">Permisar</string> <string name="option_allow">Permisar</string>
<string name="option_dismiss">Eskartar</string> <string name="option_dismiss">Eskartar</string>
<string name="nominate_for_deletion_done">Facita</string>
<string name="send_thank_success_title">Sendanta danko: Suceso</string>
<string name="send_thank_success_message">Danko sendita sucese a %1$s</string>
<string name="send_thank_failure_message">Faliis pri sendar danko a %1$s</string>
<string name="send_thank_failure_title">Sendanta danko: Falio</string>
<string name="send_thank_toast">Sendanta danko a %1$s</string>
<string name="review_copyright">Ka to obedias la reguli pri autoroyuro?</string>
<string name="review_category">Ka lua kategorio esas korekta?</string>
<string name="review_thanks">Ka vu deziras dankar la kontributero?</string>
<string name="review_spam_explanation">Kliktez NO por indikar ca imajo por efaco, se ol ne havas irga utileso.</string>
<string name="review_no_category">Ho, to ne mem havas kategorio!</string>
<string name="review_category_explanation">Ca imajo havas %1$s kategorii.</string>
<string name="review_spam_report_question">Ol esas kontre la skopo, nam ol esas</string>
<string name="review_c_violation_report_question">To esas violaco di autoroyuro, nam ol esas</string>
<string name="review_thanks_yes_button_text">Sequanta imajo</string> <string name="review_thanks_yes_button_text">Sequanta imajo</string>
<string name="review_thanks_no_button_text">Yes, pro quo ne?</string> <string name="review_thanks_no_button_text">Yes, pro quo ne?</string>
<string name="skip_image_explanation">Kliktanta ca butono donos a vu altra imajo recente sendita a Wikimedia Commons</string>
<string name="review_image_explanation">Vu povas revizar imaji, por plubonigar la qualeso di Wikimedia Commons.\nLa tri revizo-parametri esas:\n\n- Kad ica imajo havas havas irga relato kun la kuntexto?\nKande tu kliktas NO, vu adjuntos indiko (shablono) por ke ol efacesos.\n\n- Kad ica imajo violacas autoroyuro?\nSe tu klitos YES, vu adjuntos indiko por ke ol efacesos.\n\n- Kad la kategorii di ica imajo esas korekta?\nSe tu kliktos NO, vu adjuntos demando pri adjuntar korekta kategorio ad ol.\n\nSe omno esas korekta, nula shablono adjuntesos al imajo, e vu povos dankar la persono qua sendis ol.</string> <string name="review_image_explanation">Vu povas revizar imaji, por plubonigar la qualeso di Wikimedia Commons.\nLa tri revizo-parametri esas:\n\n- Kad ica imajo havas havas irga relato kun la kuntexto?\nKande tu kliktas NO, vu adjuntos indiko (shablono) por ke ol efacesos.\n\n- Kad ica imajo violacas autoroyuro?\nSe tu klitos YES, vu adjuntos indiko por ke ol efacesos.\n\n- Kad la kategorii di ica imajo esas korekta?\nSe tu kliktos NO, vu adjuntos demando pri adjuntar korekta kategorio ad ol.\n\nSe omno esas korekta, nula shablono adjuntesos al imajo, e vu povos dankar la persono qua sendis ol.</string>
<string name="no_image">Nula imajo uzita</string>
<string name="no_image_reverted">Nula imajo desfacita</string>
<string name="no_image_uploaded">Nula imajo sendita</string>
<string name="no_notification">Vu havas nul avizi sen lektar</string> <string name="no_notification">Vu havas nul avizi sen lektar</string>
<string name="no_read_notification">Vu ne lektis irga avizo</string> <string name="no_read_notification">Vu ne lektis irga avizo</string>
<string name="check_your_email_inbox">Verifikez vua e-postal adreso</string>
<string name="menu_option_read">Vidar lektita</string> <string name="menu_option_read">Vidar lektita</string>
<string name="menu_option_unread">Vidar ne-lektata</string> <string name="menu_option_unread">Vidar ne-lektata</string>
<string name="error_occurred_in_picking_images">Eventis eroro dum selekto di imaji</string>
<string name="please_wait">Vartez...</string> <string name="please_wait">Vartez...</string>
<string name="copied_successfully">Kopiita</string> <string name="copied_successfully">Kopiita</string>
<string name="welcome_do_upload_content_description">Exempli pri bona imaji por sendar a Commons</string> <string name="welcome_do_upload_content_description">Exempli pri bona imaji por sendar a Commons</string>
@ -431,7 +470,9 @@
<string name="delete_helper_show_deletion_message_else">Ne povis demandar efaco.</string> <string name="delete_helper_show_deletion_message_else">Ne povis demandar efaco.</string>
<string name="delete_helper_ask_spam_blurry">komplete neklara</string> <string name="delete_helper_ask_spam_blurry">komplete neklara</string>
<string name="delete_helper_ask_reason_copyright_press_photo">Fotografuro de komunikilaro</string> <string name="delete_helper_ask_reason_copyright_press_photo">Fotografuro de komunikilaro</string>
<string name="delete_helper_ask_reason_copyright_internet_photo">Hazarda imajo de Interreto</string>
<string name="delete_helper_ask_reason_copyright_logo">Emblemo</string> <string name="delete_helper_ask_reason_copyright_logo">Emblemo</string>
<string name="delete_helper_ask_reason_copyright_no_freedom_of_panorama">Brecho di Libereso di Panoramo</string>
<string name="delete_helper_ask_alert_set_positive_button_reason">Pro ke ol esas</string> <string name="delete_helper_ask_alert_set_positive_button_reason">Pro ke ol esas</string>
<string name="category_edit_helper_make_edit_toast">Probanta aktualigar kategorii.</string> <string name="category_edit_helper_make_edit_toast">Probanta aktualigar kategorii.</string>
<string name="category_edit_helper_show_edit_title">Aktualigo di kategorio</string> <string name="category_edit_helper_show_edit_title">Aktualigo di kategorio</string>
@ -442,7 +483,14 @@
</plurals> </plurals>
<string name="category_edit_helper_edit_message_else">Ne povis adjuntar kategorii.</string> <string name="category_edit_helper_edit_message_else">Ne povis adjuntar kategorii.</string>
<string name="category_edit_button_text">Aktualigar kategorii</string> <string name="category_edit_button_text">Aktualigar kategorii</string>
<string name="depictions_edit_helper_make_edit_toast">Probanta aktualigar deskripturi.</string>
<string name="depictions_edit_helper_show_edit_title">Redaktar deskripturi</string> <string name="depictions_edit_helper_show_edit_title">Redaktar deskripturi</string>
<plurals name="depictions_edit_helper_show_edit_message_if">
<item quantity="one">Deskripturo %1$s adjuntesis.</item>
<item quantity="other">Deskripturi %1$s adjuntesis.</item>
</plurals>
<string name="depictions_edit_helper_edit_message_else">Ne povis adjuntar deskripturi.</string>
<string name="coordinates_edit_helper_make_edit_toast">Probanta aktualigar koordinati.</string>
<string name="coordinates_edit_helper_show_edit_title">Aktualigo di koordinati</string> <string name="coordinates_edit_helper_show_edit_title">Aktualigo di koordinati</string>
<string name="description_edit_helper_show_edit_title">Aktualigo di deskripturo</string> <string name="description_edit_helper_show_edit_title">Aktualigo di deskripturo</string>
<string name="caption_edit_helper_show_edit_title">Aktualigo di surskriburo</string> <string name="caption_edit_helper_show_edit_title">Aktualigo di surskriburo</string>
@ -451,8 +499,14 @@
<string name="description_edit_helper_show_edit_message">Adjuntesis deskripturi.</string> <string name="description_edit_helper_show_edit_message">Adjuntesis deskripturi.</string>
<string name="caption_edit_helper_show_edit_message">Surskriburo adjuntesis.</string> <string name="caption_edit_helper_show_edit_message">Surskriburo adjuntesis.</string>
<string name="coordinates_edit_helper_edit_message_else">Ne povis adjuntar koordinati.</string> <string name="coordinates_edit_helper_edit_message_else">Ne povis adjuntar koordinati.</string>
<string name="description_edit_helper_edit_message_else">Ne povis adjuntar deskripturi.</string>
<string name="caption_edit_helper_edit_message_else">Ne povis adjuntar deskripturo.</string>
<string name="coordinates_picking_unsuccessful">Koordinati dil imajo ne aktualigesis</string>
<string name="descriptions_picking_unsuccessful">Ne povis obtenar deskripturi.</string>
<string name="description_activity_title">Redaktar deskripturi ed informo-texti</string>
<string name="share_image_via">Partigar imajo uzanta</string> <string name="share_image_via">Partigar imajo uzanta</string>
<string name="you_have_no_achievements_yet">Vu ankore ne facis kontributaji</string> <string name="you_have_no_achievements_yet">Vu ankore ne facis kontributaji</string>
<string name="no_achievements_yet">%s ankore ne facis irga kontributado</string>
<string name="account_created">Konto kreesis!</string> <string name="account_created">Konto kreesis!</string>
<string name="text_copy">Texto kopiita a \'\'clipboard\'\'</string> <string name="text_copy">Texto kopiita a \'\'clipboard\'\'</string>
<string name="notification_mark_read">Mesajo indikita kom \'lektita\'</string> <string name="notification_mark_read">Mesajo indikita kom \'lektita\'</string>
@ -462,17 +516,21 @@
<string name="place_state_needs_photo">Bezonas fotografuro</string> <string name="place_state_needs_photo">Bezonas fotografuro</string>
<string name="place_type">Tipo di lokizo:</string> <string name="place_type">Tipo di lokizo:</string>
<string name="nearby_search_hint">Ponto, muzeo, hotelo, edc.</string> <string name="nearby_search_hint">Ponto, muzeo, hotelo, edc.</string>
<string name="you_must_reset_your_passsword" fuzzy="true">Irgu ne funcionis dum \'\'log in\'\'. Vu mustos ridefinar vua pasovorto!!</string> <string name="you_must_reset_your_passsword">Irgu faliis dum \'\'log in\'\'. Vu mustos ridefinar vua pasovorto!!</string>
<string name="title_for_media">\'\'MEDIA\'\'</string> <string name="title_for_media">\'\'MEDIA\'\'</string>
<string name="title_for_child_classes">SUBKLASI</string> <string name="title_for_child_classes">SUBKLASI</string>
<string name="title_for_parent_classes">KLASI PLU ABSTRAKTA</string> <string name="title_for_parent_classes">KLASI PLU ABSTRAKTA</string>
<string name="upload_nearby_place_found_title">Loko proxima trovesis</string> <string name="upload_nearby_place_found_title">Loko proxima trovesis</string>
<string name="upload_nearby_place_found_description_singular" fuzzy="true">Ka to esas fotografuro pri %1$s?</string> <string name="upload_nearby_place_found_description_plural">Ka ca imaji apartenas a %1$s?</string>
<string name="upload_nearby_place_found_description_singular">Ka to esas imajo di %1$s?</string>
<string name="title_app_shortcut_bookmark">Marko-rubandi</string> <string name="title_app_shortcut_bookmark">Marko-rubandi</string>
<string name="title_app_shortcut_setting">Ajusti</string> <string name="title_app_shortcut_setting">Ajusti</string>
<string name="remove_bookmark">Efacita de la marko-rubandi</string>
<string name="add_bookmark">Adjuntita marko-rubandi</string> <string name="add_bookmark">Adjuntita marko-rubandi</string>
<string name="wallpaper_set_unsuccessfully">Irgu faliis. Ne povis vidar la muropapero</string>
<string name="setting_wallpaper_dialog_title">Uzar kom skreno-kovrilo</string> <string name="setting_wallpaper_dialog_title">Uzar kom skreno-kovrilo</string>
<string name="setting_wallpaper_dialog_message">Kreanta skreno-kovrilo. Voluntez vartar...</string> <string name="setting_wallpaper_dialog_message">Kreanta skreno-kovrilo. Voluntez vartar...</string>
<string name="theme_default_name">Sequar sistemo</string>
<string name="theme_dark_name">Koloro obskura</string> <string name="theme_dark_name">Koloro obskura</string>
<string name="theme_light_name">Koloro klara</string> <string name="theme_light_name">Koloro klara</string>
<string name="load_more">Charjez pluse</string> <string name="load_more">Charjez pluse</string>
@ -492,6 +550,9 @@
<string name="leaderboard_column_user">Uzero</string> <string name="leaderboard_column_user">Uzero</string>
<string name="leaderboard_column_count">Quanteso</string> <string name="leaderboard_column_count">Quanteso</string>
<string name="setting_avatar_dialog_title">Uzar kom \'\'avatar\'\' di la tabelo pri precipua kunlaboranti</string> <string name="setting_avatar_dialog_title">Uzar kom \'\'avatar\'\' di la tabelo pri precipua kunlaboranti</string>
<string name="avatar_set_successfully">Ajusto di avataro</string>
<string name="avatar_set_unsuccessfully">Eroro dum ajusto di nov avataro, voluntez probar itere</string>
<string name="menu_set_avatar">Uzar kom avataro</string>
<string name="leaderboard_yearly">Yare</string> <string name="leaderboard_yearly">Yare</string>
<string name="leaderboard_weekly">Semanale</string> <string name="leaderboard_weekly">Semanale</string>
<string name="leaderboard_all_time">Sempre</string> <string name="leaderboard_all_time">Sempre</string>
@ -502,9 +563,16 @@
<string name="statistics_quality">Imaji di qualeso</string> <string name="statistics_quality">Imaji di qualeso</string>
<string name="cancelling_upload">Nuliganta sendajo...</string> <string name="cancelling_upload">Nuliganta sendajo...</string>
<string name="cancel_upload">Cesar kargajo</string> <string name="cancel_upload">Cesar kargajo</string>
<string name="depicts_step_title">Montras</string>
<string name="license_step_title">Licencizo di \'\'media\'\'</string>
<string name="media_detail_step_title">Detali pri \'\'media\'\'</string>
<string name="menu_view_category_page">Vidar kategorio-pagino</string>
<string name="menu_view_item_page">Vidar pagino dil arkivo</string>
<string name="read_help_link">Lektez pluse</string> <string name="read_help_link">Lektez pluse</string>
<string name="media_detail_in_all_languages">En omna idiomi</string> <string name="media_detail_in_all_languages">En omna idiomi</string>
<string name="choose_a_location">Selektez lokizo</string> <string name="choose_a_location">Selektez lokizo</string>
<string name="select_location_location_picker">Selektar lokizo</string>
<string name="show_in_map_app">Montrar en l\'utensilo \'\'app\'\' di mapo</string>
<string name="modify_location">Aktualigar lokizo</string> <string name="modify_location">Aktualigar lokizo</string>
<string name="image_location">Lokizo dil imajo</string> <string name="image_location">Lokizo dil imajo</string>
<string name="check_whether_location_is_correct">Verifikez se la lokizo esas korekta</string> <string name="check_whether_location_is_correct">Verifikez se la lokizo esas korekta</string>
@ -524,8 +592,33 @@
<string name="display_monuments">Montrez monumenti</string> <string name="display_monuments">Montrez monumenti</string>
<string name="learn_more">SAVEZ PLUSE</string> <string name="learn_more">SAVEZ PLUSE</string>
<string name="need_permission">Bezonas permiso</string> <string name="need_permission">Bezonas permiso</string>
<string name="menu_view_user_page" fuzzy="true">Vidar uzeropagino</string> <string name="contributions_of_user">Kontributadi dil uzero: %s</string>
<string name="achievements_of_user">Sucesi dil uzero: %s</string>
<string name="menu_view_user_page">Vidar profilo dil uzero</string>
<string name="edit_depictions">Redaktar deskripturi</string>
<string name="edit_categories">Redaktar kategorii</string>
<string name="advanced_options">Progresiva selektaji (advanced options)</string>
<string name="apply">Aplikar</string>
<string name="reset">Restaurar</string>
<string name="no_location_found_title">Nula lokizo trovita</string>
<string name="no_location_found_message">Ka vu deziras informar la loko de ube vu obtenis ca imajo?\nInformo pri la lokizo helpos editeri trovar vua imajo, do ol divenos plu utila.\nDanko!</string> <string name="no_location_found_message">Ka vu deziras informar la loko de ube vu obtenis ca imajo?\nInformo pri la lokizo helpos editeri trovar vua imajo, do ol divenos plu utila.\nDanko!</string>
<string name="add_location">Adjuntez lokizo</string>
<string name="explore_map_details">Detali</string>
<string name="api_level">nivelo di API</string>
<string name="android_version">versiono di Android</string>
<string name="device_manufacturer">Fabrikanto dil aparato</string>
<string name="device_model">Modelo dil aparato</string>
<string name="device_name">Nomo dil aparato</string>
<string name="network_type">Tipo di reto</string>
<string name="thanks_feedback">Danko por sendar vua opiniono</string>
<string name="error_feedback">Eroro dum sendo di respondo</string>
<string name="enter_description">Qual es vua opiniono (feedback)?</string>
<string name="your_feedback">Vua opiniono (\'\'feedback\'\')</string>
<string name="mark_as_not_for_upload">Indikez por ne sendar ol</string>
<string name="unmark_as_not_for_upload">Itere indikez por sendar ol</string>
<string name="marking_as_not_for_upload">Indikanta ke ol ne sendesos</string>
<string name="this_image_is_already_uploaded">Ca imajo ja sendesis</string>
<string name="can_not_select_this_image_for_upload">Ne povis selektar ca imajo por sendar (\'\'upload\'\')</string>
<string name="image_selected">Imajo selektita</string> <string name="image_selected">Imajo selektita</string>
<string name="image_marked_as_not_for_upload">Ca imajo indikesis por ne sendesar</string> <string name="image_marked_as_not_for_upload">Ca imajo indikesis por ne sendesar</string>
<string name="menu_view_report">Raporto</string> <string name="menu_view_report">Raporto</string>
@ -535,13 +628,49 @@
<string name="report_user">Avizar ca uzero</string> <string name="report_user">Avizar ca uzero</string>
<string name="report_content">Informar ca kontenajo</string> <string name="report_content">Informar ca kontenajo</string>
<string name="request_user_block">Demandar blokuso di ca uzero</string> <string name="request_user_block">Demandar blokuso di ca uzero</string>
<string name="full_screen_mode_zoom_info">Uzez du fingri por augmentar o diminutar \'\'zoom\'\'.</string>
<string name="similar_coordinate_description_auto_set">Koordinati ne esas l\'exakta, tamen l\'individuo qua sendis ca imajo kredas ke la koordinati quin lu informis esas suficante proxima.</string> <string name="similar_coordinate_description_auto_set">Koordinati ne esas l\'exakta, tamen l\'individuo qua sendis ca imajo kredas ke la koordinati quin lu informis esas suficante proxima.</string>
<string name="edit_image">Modifikar imajo</string> <string name="edit_image">Modifikar imajo</string>
<string name="edit_location">Aktualigar lokizo</string> <string name="edit_location">Aktualigar lokizo</string>
<string name="location_updated">Lokizo aktualigita!</string>
<string name="remove_location">Removar lokizo</string>
<string name="remove_location_warning_title">Removar avizo pri lokizo</string>
<string name="location_removed">Lokizo efacita!</string>
<string name="send_thanks_to_author">Dankar l\'autoro</string> <string name="send_thanks_to_author">Dankar l\'autoro</string>
<string name="error_sending_thanks">Eroro sendanta danki al autoro.</string> <string name="error_sending_thanks">Eroro sendanta danki al autoro.</string>
<string name="invalid_login_message">La tempo-quanto por vua \'\'log in\'\' finis. Voluntez itere enirar.</string>
<string name="file_saved_successfully">Konservo sucesoza di arkivo</string>
<string name="do_you_want_to_open_gpx_file">Ka vu deziras apertar arkivo GPX?</string>
<string name="do_you_want_to_open_kml_file">Ka vu deziras apartar l\'arkivo KML?</string>
<string name="failed_to_save_kml_file">Faliis pri konservar arkivo KML.</string>
<string name="failed_to_save_gpx_file">Faliis pri konservar arkivo GPX.</string>
<string name="saving_kml_file">Konservanta arqkivo KML</string>
<string name="saving_gpx_file">Konservanta arkivo GPX</string>
<plurals name="custom_picker_images_selected_title_appendix"> <plurals name="custom_picker_images_selected_title_appendix">
<item quantity="one">%d imajo selektita</item> <item quantity="one">%d imajo selektita</item>
<item quantity="other">%d imaji selektita</item> <item quantity="other">%d imaji selektita</item>
</plurals> </plurals>
<string name="talk">Diskuto</string>
<string name="write_something_about_the_item">Dicez irgu pri l\'arkivo \'%1$s\'. Ol esos videbla publike.</string>
<string name="uploads">Arkivi sendita</string>
<string name="pending">Vartanta</string>
<string name="failed">Faliis</string>
<string name="could_not_load_place_data">Ne povis inkluzar datumi pri la loko</string>
<string name="custom_selector_delete_folder">Efacar faldilo</string>
<string name="custom_selector_confirm_deletion_title">Konfirmez efaco</string>
<string name="custom_selector_confirm_deletion_message">Ka vu deziras efacar faldilo %1$s, kontenanta %2$d arkivi?</string>
<string name="custom_selector_delete">Efacez</string>
<string name="custom_selector_cancel">Nuligez</string>
<string name="custom_selector_folder_deleted_success">Faldilo %1$s sucese efacita</string>
<string name="custom_selector_folder_deleted_failure">Faliis pri efacar faldilo %1$s</string>
<string name="usages_on_commons_heading">Commons</string>
<string name="usages_on_other_wikis_heading">Altra wiki</string>
<string name="file_usages_container_heading">Uzi dil arkivo</string>
<string name="title_activity_single_web_view">SingleWebViewActivity</string>
<string name="account">Konto</string>
<string name="vanish_account">Efacar konto</string>
<string name="account_vanish_request_confirm_title">Avizo pri efaco di konto</string>
<string name="caption">Deskripto-texto</string>
<string name="caption_copied_to_clipboard">Deskripto-texto kopiita a \'\'clipboard\'\'</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Gratuli! Omna imaji en ca albumo sive sendesis, sive indikesis por ne sendar.</string>
</resources> </resources>

View file

@ -85,7 +85,7 @@
<item quantity="many">מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך</item> <item quantity="many">מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך</item>
<item quantity="other">מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך</item> <item quantity="other">מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך</item>
</plurals> </plurals>
<string name="navigation_item_explore">לחקור</string> <string name="navigation_item_explore">סיור</string>
<string name="preference_category_appearance">מראה</string> <string name="preference_category_appearance">מראה</string>
<string name="preference_category_general">כללי</string> <string name="preference_category_general">כללי</string>
<string name="preference_category_feedback">משוב</string> <string name="preference_category_feedback">משוב</string>
@ -311,7 +311,7 @@
<string name="nearby_showing_pins_offline">האינטרנט אינו זמין. מוצגים רק מקומות שמורים.</string> <string name="nearby_showing_pins_offline">האינטרנט אינו זמין. מוצגים רק מקומות שמורים.</string>
<string name="upload_location_access_denied">הגישה למיקום נדחתה. נא להגדיר את המקום שלך ידנית כדי להשתמש ביכולת הזאת.</string> <string name="upload_location_access_denied">הגישה למיקום נדחתה. נא להגדיר את המקום שלך ידנית כדי להשתמש ביכולת הזאת.</string>
<string name="location_permission_rationale_nearby">נדרשת הרשאה כדי להציג רשימה של מקומות בסביבה</string> <string name="location_permission_rationale_nearby">נדרשת הרשאה כדי להציג רשימה של מקומות בסביבה</string>
<string name="location_permission_rationale_explore">נדרשת הרשאה להצגת רשימת התמונות בסביבתך</string> <string name="location_permission_rationale_explore">נדרשת הרשאה כדי להציג רשימה של תמונות בסביבה</string>
<string name="nearby_directions">כיוונים</string> <string name="nearby_directions">כיוונים</string>
<string name="nearby_wikidata">ויקינתונים</string> <string name="nearby_wikidata">ויקינתונים</string>
<string name="nearby_wikipedia">ויקיפדיה</string> <string name="nearby_wikipedia">ויקיפדיה</string>
@ -605,12 +605,12 @@
<string name="place_state_exists">קיים</string> <string name="place_state_exists">קיים</string>
<string name="place_state_needs_photo">זקוק לתצלום</string> <string name="place_state_needs_photo">זקוק לתצלום</string>
<string name="place_type">סוג המקום:</string> <string name="place_type">סוג המקום:</string>
<string name="nearby_search_hint">גשר, מוזאון, מלון, וכו\'.</string> <string name="nearby_search_hint">גשר, מוזאון, מלון וכו\'.</string>
<string name="you_must_reset_your_passsword">משהו השתבש בכניסה למערכת, עליך לאפס את הסיסמה שלך!</string> <string name="you_must_reset_your_passsword">משהו השתבש בכניסה למערכת, עליך לאפס את הסיסמה שלך!</string>
<string name="title_for_media">מדיה</string> <string name="title_for_media">מדיה</string>
<string name="title_for_child_classes">מחלקות יורשות</string> <string name="title_for_child_classes">מחלקות יורשות</string>
<string name="title_for_parent_classes">מחלקות מורישות</string> <string name="title_for_parent_classes">מחלקות מורישות</string>
<string name="upload_nearby_place_found_title">נמצא בקרבת מקום</string> <string name="upload_nearby_place_found_title">נמצא מקום בסביבה</string>
<string name="upload_nearby_place_found_description_plural">האם אלו תמונות של %1$s?</string> <string name="upload_nearby_place_found_description_plural">האם אלו תמונות של %1$s?</string>
<string name="upload_nearby_place_found_description_singular">האם זאת תמונה של %1$s?</string> <string name="upload_nearby_place_found_description_singular">האם זאת תמונה של %1$s?</string>
<string name="title_app_shortcut_bookmark">סימניות</string> <string name="title_app_shortcut_bookmark">סימניות</string>
@ -628,7 +628,7 @@
<string name="ask_to_turn_location_on">להפעיל מיקום?</string> <string name="ask_to_turn_location_on">להפעיל מיקום?</string>
<string name="ask_to_turn_location_on_text">נא להפעיל את שירותי המיקום כדי שהיישום יוכל להציג את מיקומך הנוכחי</string> <string name="ask_to_turn_location_on_text">נא להפעיל את שירותי המיקום כדי שהיישום יוכל להציג את מיקומך הנוכחי</string>
<string name="nearby_needs_location">פעולת \"בסביבה\" זקוקה לשירותי מיקומי פועלים כדי לעבוד כמו שצריך</string> <string name="nearby_needs_location">פעולת \"בסביבה\" זקוקה לשירותי מיקומי פועלים כדי לעבוד כמו שצריך</string>
<string name="explore_map_needs_location">חקירת המפה דורשת הרשאות מיקום כדי להציג תמונות בסביבתך</string> <string name="explore_map_needs_location">מפת \"סיור\" דורשת הרשאות מיקום כדי להציג תמונות בסביבתך</string>
<string name="upload_map_location_access">יש להעניק הרשאת מיקום כדי להגדיר את המיקום אוטומטית.</string> <string name="upload_map_location_access">יש להעניק הרשאת מיקום כדי להגדיר את המיקום אוטומטית.</string>
<string name="use_location_from_similar_image">האם צילמת את שתי התמונות באותו המקום? האם ברצונך להשתמש בקו הרוחב וקו האורך של התמונה משמאל?</string> <string name="use_location_from_similar_image">האם צילמת את שתי התמונות באותו המקום? האם ברצונך להשתמש בקו הרוחב וקו האורך של התמונה משמאל?</string>
<string name="load_more">לטעון עוד</string> <string name="load_more">לטעון עוד</string>
@ -851,4 +851,7 @@
<string name="account_vanish_request_confirm_title">אזהרת העלמת חשבון</string> <string name="account_vanish_request_confirm_title">אזהרת העלמת חשבון</string>
<string name="caption">כותרת</string> <string name="caption">כותרת</string>
<string name="caption_copied_to_clipboard">הכותרת הועתקה ללוח</string> <string name="caption_copied_to_clipboard">הכותרת הועתקה ללוח</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">ברכותינו, כל התמונות באלבום הזה הועלו או שסומנו לא להעלאה.</string>
<string name="show_in_explore">בתצוגת סיור</string>
<string name="show_in_nearby">בתצוגת בסביבה</string>
</resources> </resources>

View file

@ -108,6 +108,8 @@
<string name="menu_from_camera">写真を撮影</string> <string name="menu_from_camera">写真を撮影</string>
<string name="menu_nearby">付近</string> <string name="menu_nearby">付近</string>
<string name="provider_contributions">自分のアップロード</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_share">共有</string>
<string name="menu_view_file_page">ファイルのページを表示</string> <string name="menu_view_file_page">ファイルのページを表示</string>
<string name="share_title_hint">キャプション (必須)</string> <string name="share_title_hint">キャプション (必須)</string>
@ -126,6 +128,7 @@
<string name="categories_search_text_hint">カテゴリを検索</string> <string name="categories_search_text_hint">カテゴリを検索</string>
<string name="depicts_search_text_hint">アップロードする素材の被写体を検索(山岳、タージマハールなど)</string> <string name="depicts_search_text_hint">アップロードする素材の被写体を検索(山岳、タージマハールなど)</string>
<string name="menu_save_categories">保存</string> <string name="menu_save_categories">保存</string>
<string name="menu_overflow_desc">オーバーフローメニュー</string>
<string name="refresh_button">更新</string> <string name="refresh_button">更新</string>
<string name="display_list_button">一覧</string> <string name="display_list_button">一覧</string>
<string name="contributions_subtitle_zero">(まだ何もアップロードされていません)</string> <string name="contributions_subtitle_zero">(まだ何もアップロードされていません)</string>
@ -313,6 +316,7 @@
<string name="title_activity_search">検索</string> <string name="title_activity_search">検索</string>
<string name="search_recent_header">最近の検索:</string> <string name="search_recent_header">最近の検索:</string>
<string name="provider_searches">最近、検索したクエリ</string> <string name="provider_searches">最近、検索したクエリ</string>
<string name="provider_recent_languages">最近の言語クエリ</string>
<string name="error_loading_categories">カテゴリの読み込み中にエラーが発生しました。</string> <string name="error_loading_categories">カテゴリの読み込み中にエラーが発生しました。</string>
<string name="error_loading_depictions">描写の読み込み中にエラーが発生しました</string> <string name="error_loading_depictions">描写の読み込み中にエラーが発生しました</string>
<string name="search_tab_title_media">メデイア</string> <string name="search_tab_title_media">メデイア</string>
@ -345,6 +349,7 @@
<string name="wrong">不正解</string> <string name="wrong">不正解</string>
<string name="quiz_screenshot_question">このスクリーンショットをアップロードしてもよいですか?</string> <string name="quiz_screenshot_question">このスクリーンショットをアップロードしてもよいですか?</string>
<string name="share_app_title">アプリをシェアする</string> <string name="share_app_title">アプリをシェアする</string>
<string name="rotate">回転</string>
<string name="error_fetching_nearby_places">付近の場所を読み込めません</string> <string name="error_fetching_nearby_places">付近の場所を読み込めません</string>
<string name="no_recent_searches">最近の検索はまだありません</string> <string name="no_recent_searches">最近の検索はまだありません</string>
<string name="delete_recent_searches_dialog">本当に検索履歴を消去しますか?</string> <string name="delete_recent_searches_dialog">本当に検索履歴を消去しますか?</string>
@ -355,11 +360,12 @@
<string name="delete">削除</string> <string name="delete">削除</string>
<string name="Achievements">貢献</string> <string name="Achievements">貢献</string>
<string name="Profile">プロフィール</string> <string name="Profile">プロフィール</string>
<string name="badges">バッジ</string>
<string name="statistics">統計</string> <string name="statistics">統計</string>
<string name="statistics_thanks">受け取った感謝</string> <string name="statistics_thanks">受け取った感謝</string>
<string name="statistics_featured">秀逸な画像</string> <string name="statistics_featured">秀逸な画像</string>
<string name="statistics_wikidata_edits">「近くの場所」機能でアップロードした画像</string> <string name="statistics_wikidata_edits">「近くの場所」機能でアップロードした画像</string>
<string name="level" fuzzy="true">レベル</string> <string name="level">レベル %d</string>
<string name="images_uploaded">アップロードした画像</string> <string name="images_uploaded">アップロードした画像</string>
<string name="image_reverts">却下されなかった画像</string> <string name="image_reverts">却下されなかった画像</string>
<string name="images_used_by_wiki">使用された画像</string> <string name="images_used_by_wiki">使用された画像</string>
@ -391,6 +397,7 @@
<string name="map_application_missing">お使いの機器に適したアプリが見つかりませんでした。この機能を使用できる地図アプリをインストールしてください。</string> <string name="map_application_missing">お使いの機器に適したアプリが見つかりませんでした。この機能を使用できる地図アプリをインストールしてください。</string>
<string name="title_page_bookmarks_pictures">画像</string> <string name="title_page_bookmarks_pictures">画像</string>
<string name="title_page_bookmarks_locations">位置</string> <string name="title_page_bookmarks_locations">位置</string>
<string name="title_page_bookmarks_categories">カテゴリ</string>
<string name="menu_bookmark">ブックマークに追加/から削除</string> <string name="menu_bookmark">ブックマークに追加/から削除</string>
<string name="provider_bookmarks">ブックマーク</string> <string name="provider_bookmarks">ブックマーク</string>
<string name="bookmark_empty">ブックマークは追加されていません</string> <string name="bookmark_empty">ブックマークは追加されていません</string>
@ -642,7 +649,7 @@
<string name="done">完了</string> <string name="done">完了</string>
<string name="back">戻る</string> <string name="back">戻る</string>
<string name="need_permission">権限が必要です</string> <string name="need_permission">権限が必要です</string>
<string name="menu_view_user_page" fuzzy="true">利用者ページを表示</string> <string name="menu_view_user_page">利用者プロフィールを表示</string>
<string name="edit_depictions">題材を編集する</string> <string name="edit_depictions">題材を編集する</string>
<string name="edit_categories">カテゴリを編集</string> <string name="edit_categories">カテゴリを編集</string>
<string name="apply">適用</string> <string name="apply">適用</string>
@ -683,4 +690,17 @@
<plurals name="custom_picker_images_selected_title_appendix"> <plurals name="custom_picker_images_selected_title_appendix">
<item quantity="other">%d件の画像が選択されました</item> <item quantity="other">%d件の画像が選択されました</item>
</plurals> </plurals>
<string name="cancelling_all_the_uploads">すべてのアップロードをキャンセルしています...</string>
<string name="uploads">アップロード</string>
<string name="pending">保留中</string>
<string name="failed">失敗しました</string>
<string name="custom_selector_delete">削除</string>
<string name="custom_selector_cancel">キャンセル</string>
<string name="usages_on_commons_heading">コモンズ</string>
<string name="usages_on_other_wikis_heading">その他のウィキ</string>
<string name="account">アカウント</string>
<string name="vanish_account">アカウント抹消</string>
<string name="account_vanish_request_confirm_title">アカウント抹消の警告</string>
<string name="caption">キャプション</string>
<string name="caption_copied_to_clipboard">キャプションをクリップボードにコピーしました</string>
</resources> </resources>

View file

@ -709,6 +709,7 @@
<string name="file_usages_container_heading">이 파일을 사용하는 문서</string> <string name="file_usages_container_heading">이 파일을 사용하는 문서</string>
<string name="account">계정</string> <string name="account">계정</string>
<string name="vanish_account">계정 버리기</string> <string name="vanish_account">계정 버리기</string>
<string name="account_vanish_request_confirm">버리기는 &lt;b&gt;최후의 수단&lt;/b&gt;이며 &lt;b&gt;영원히 편집을 중단하고 싶을 때&lt;/b&gt;와 가능한 한 많은 과거 연관성을 숨기고 싶을 때만 사용해야 합니다.&lt;br/&gt;&lt;br/&gt;위키미디어 공용에서 계정을 삭제하려면 계정 이름을 변경하여 다른 사람이 기여한 내용을 알아볼 수 없도록 하는 계정 버리기라는 프로세스를 거쳐야 합니다. &lt;b&gt;버리기는 완전한 익명성을 보장하지 않으며 프로젝트에 수행한 기여를 제거하지 않습니다&lt;/b&gt;.</string>
<string name="caption">캡션</string> <string name="caption">캡션</string>
<string name="caption_copied_to_clipboard">캡션이 클립보드에 복사되었습니다</string> <string name="caption_copied_to_clipboard">캡션이 클립보드에 복사되었습니다</string>
</resources> </resources>

View file

@ -96,6 +96,8 @@
<string name="menu_from_camera">Nufotografuoti</string> <string name="menu_from_camera">Nufotografuoti</string>
<string name="menu_nearby">Netoliese</string> <string name="menu_nearby">Netoliese</string>
<string name="provider_contributions">Mano įkėlimai</string> <string name="provider_contributions">Mano įkėlimai</string>
<string name="menu_copy_link">Kopijuoti nuorodą</string>
<string name="menu_link_copied">Nuoroda nukopijuota į mainų sritį.</string>
<string name="menu_share">Dalintis</string> <string name="menu_share">Dalintis</string>
<string name="menu_view_file_page">Žiūrėti failo puslapį</string> <string name="menu_view_file_page">Žiūrėti failo puslapį</string>
<string name="share_title_hint">Antraštė (būtina)</string> <string name="share_title_hint">Antraštė (būtina)</string>
@ -345,11 +347,13 @@
<string name="delete">Ištrinti</string> <string name="delete">Ištrinti</string>
<string name="Achievements">Pasiekimai</string> <string name="Achievements">Pasiekimai</string>
<string name="Profile">Profilis</string> <string name="Profile">Profilis</string>
<string name="badges">Ženkliukai</string>
<string name="statistics">Statistika</string> <string name="statistics">Statistika</string>
<string name="statistics_thanks">Gauta padėka</string> <string name="statistics_thanks">Gauta padėka</string>
<string name="statistics_featured">Rinktiniai paveikslėliai</string> <string name="statistics_featured">Rinktiniai paveikslėliai</string>
<string name="statistics_wikidata_edits">Vaizdai per „Netoliese esančios vietos“</string> <string name="statistics_wikidata_edits">Vaizdai per „Netoliese esančios vietos“</string>
<string name="level" fuzzy="true">Lygis</string> <string name="level">Lygis %d</string>
<string name="profileLevel">%s (%s lygis)</string>
<string name="images_uploaded">Vaizdai įkelti</string> <string name="images_uploaded">Vaizdai įkelti</string>
<string name="image_reverts">Paveikslėliai negrąžinti</string> <string name="image_reverts">Paveikslėliai negrąžinti</string>
<string name="images_used_by_wiki">Naudoti vaizdai</string> <string name="images_used_by_wiki">Naudoti vaizdai</string>
@ -381,6 +385,7 @@
<string name="map_application_missing">Jūsų įrenginyje nepavyko rasti suderinamos žemėlapio programos. Norėdami naudotis šia funkcija, įdiekite žemėlapio programą.</string> <string name="map_application_missing">Jūsų įrenginyje nepavyko rasti suderinamos žemėlapio programos. Norėdami naudotis šia funkcija, įdiekite žemėlapio programą.</string>
<string name="title_page_bookmarks_pictures">Nuotraukos</string> <string name="title_page_bookmarks_pictures">Nuotraukos</string>
<string name="title_page_bookmarks_locations">Vietos</string> <string name="title_page_bookmarks_locations">Vietos</string>
<string name="title_page_bookmarks_categories">Kategorijos</string>
<string name="menu_bookmark">Pridėti prie / pašalinti iš žymių</string> <string name="menu_bookmark">Pridėti prie / pašalinti iš žymių</string>
<string name="provider_bookmarks">Žymės</string> <string name="provider_bookmarks">Žymės</string>
<string name="bookmark_empty">Jūs nepridėjote jokių žymių</string> <string name="bookmark_empty">Jūs nepridėjote jokių žymių</string>
@ -751,4 +756,18 @@
<string name="pending">Laukiama</string> <string name="pending">Laukiama</string>
<string name="failed">Nepavyko</string> <string name="failed">Nepavyko</string>
<string name="could_not_load_place_data">Nepavyko įkelti vietos duomenų</string> <string name="could_not_load_place_data">Nepavyko įkelti vietos duomenų</string>
<string name="custom_selector_delete_folder">Trinti aplanką</string>
<string name="custom_selector_confirm_deletion_title">Patvirtinti ištrynimą</string>
<string name="custom_selector_confirm_deletion_message">Ar tikrai norite ištrinti aplanką %1$s, kuriame yra %2$d elementų?</string>
<string name="custom_selector_delete">Ištrinti</string>
<string name="custom_selector_cancel">Atšaukti</string>
<string name="custom_selector_folder_deleted_success">Aplankas %1$s sėkmingai ištrintas</string>
<string name="custom_selector_folder_deleted_failure">Nepavyko ištrinti aplanko %1$s</string>
<string name="custom_selector_error_trashing_folder_contents">Klaida išsiunčiant į šiukšliadėžę aplanko turinį: %1$s</string>
<string name="error_while_loading">Įkeliant įvyko klaida</string>
<string name="no_usages_found">Naudojimo būdų nerasta</string>
<string name="usages_on_commons_heading">Vikiteka</string>
<string name="usages_on_other_wikis_heading">Kiti viki</string>
<string name="file_usages_container_heading">Failo naudojimas</string>
<string name="account">Paskyra</string>
</resources> </resources>

View file

@ -817,4 +817,7 @@
<string name="account_vanish_request_confirm">Исчезнувањето е &lt;b&gt;крајна мерка&lt;/b&gt; и треба да се користи само ако сакате да престанете да уредувате засекогаш/b&gt; и да скриете што повеќе од вашите досегашни врски.&lt;br/&gt;&lt;br/&gt;Бришењето сметки на Википедија се врши со менување на името на вашата сметка, така што другите не би можеле да ги препознаат вашите придонеси во постапка наречена „исчезнување“ на сметка.&lt;b&gt;Исчезнувањето не гарантира целосна анонимност и не ги отстранува придонесите на проектите&lt;/b&gt;.</string> <string name="account_vanish_request_confirm">Исчезнувањето е &lt;b&gt;крајна мерка&lt;/b&gt; и треба да се користи само ако сакате да престанете да уредувате засекогаш/b&gt; и да скриете што повеќе од вашите досегашни врски.&lt;br/&gt;&lt;br/&gt;Бришењето сметки на Википедија се врши со менување на името на вашата сметка, така што другите не би можеле да ги препознаат вашите придонеси во постапка наречена „исчезнување“ на сметка.&lt;b&gt;Исчезнувањето не гарантира целосна анонимност и не ги отстранува придонесите на проектите&lt;/b&gt;.</string>
<string name="caption">Толкување</string> <string name="caption">Толкување</string>
<string name="caption_copied_to_clipboard">Толкувањето е ставено во меѓускладот</string> <string name="caption_copied_to_clipboard">Толкувањето е ставено во меѓускладот</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Честитаме. Сите слики од овој албум се подигнати или обележани за неподигање.</string>
<string name="show_in_explore">Прикажи во „Истражи“</string>
<string name="show_in_nearby">Прикажи во „Во близина“</string>
</resources> </resources>

View file

@ -3,13 +3,31 @@
* Adithyak1997 * Adithyak1997
* Akhilan * Akhilan
* Jacob.jose * Jacob.jose
* Jinoytommanjaly
* Kiran Gopi * Kiran Gopi
* Praveenp * Praveenp
* Santhosh.thottingal * Santhosh.thottingal
--> -->
<resources> <resources>
<string name="commons_facebook">കോമൺസ് ഫേസ്ബുക്ക് പേജ്</string>
<string name="commons_github">കോമൺസ് ജിത്ഹബ് സോഴ്സ് കോഡ്</string>
<string name="commons_logo">കോമൺസ് ലോഗോ</string> <string name="commons_logo">കോമൺസ് ലോഗോ</string>
<string name="commons_website">കോമൺസ് വെബ്‌സൈറ്റ്</string> <string name="commons_website">കോമൺസ് വെബ്‌സൈറ്റ്</string>
<string name="submit">സമർപ്പിക്കുക</string>
<string name="add_another_description">മറ്റൊരു വിവരണം ചേർക്കുക</string>
<string name="add_new_contribution">പുതിയ സംഭാവന ചേർക്കുക</string>
<string name="add_contribution_from_camera">ക്യാമറയിൽ നിന്നുള്ള സംഭാവന ചേർക്കുക</string>
<string name="add_contribution_from_photos">ഫോട്ടോകളിൽ നിന്നുള്ള സംഭാവന ചേർക്കുക</string>
<string name="add_contribution_from_contributions_gallery">മുമ്പത്തെ സംഭാവനകളുടെ ഗാലറിയിൽ നിന്നുള്ള സംഭാവന ചേർക്കുക</string>
<string name="show_captions">തലവാചകം</string>
<string name="row_item_language_description">ഭാഷാ വിവരണം</string>
<string name="row_item_caption">തലവാചകം</string>
<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="appwidget_img">ദിവസത്തെ ചിത്രം</string>
<plurals name="uploads_pending_notification_indicator"> <plurals name="uploads_pending_notification_indicator">
<item quantity="one">ഒരു പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നു</item> <item quantity="one">ഒരു പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നു</item>
<item quantity="other">%1$d പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യുന്നു</item> <item quantity="other">%1$d പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യുന്നു</item>
@ -19,6 +37,7 @@
<item quantity="one">ഒരു അപ്‌ലോഡ്</item> <item quantity="one">ഒരു അപ്‌ലോഡ്</item>
<item quantity="other">%1$d അപ്‌ലോഡുകൾ</item> <item quantity="other">%1$d അപ്‌ലോഡുകൾ</item>
</plurals> </plurals>
<string name="starting_uploads">അപ്‌ലോഡുകൾ ആരംഭിക്കുന്നു</string>
<plurals name="starting_multiple_uploads" fuzzy="true"> <plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one">ഒരു അപ്‌ലോഡ് തുടങ്ങുന്നു</item> <item quantity="one">ഒരു അപ്‌ലോഡ് തുടങ്ങുന്നു</item>
<item quantity="other">%1$d അപ്‌ലോഡുകൾ തുടങ്ങുന്നു</item> <item quantity="other">%1$d അപ്‌ലോഡുകൾ തുടങ്ങുന്നു</item>
@ -35,6 +54,8 @@
<string name="preference_category_privacy">സ്വകാര്യത</string> <string name="preference_category_privacy">സ്വകാര്യത</string>
<string name="app_name">കോമൺസ്</string> <string name="app_name">കോമൺസ്</string>
<string name="menu_settings">സജ്ജീകരണങ്ങൾ</string> <string name="menu_settings">സജ്ജീകരണങ്ങൾ</string>
<string name="intent_share_upload_label">കോമൺസിലേക്ക് അപ്‌ലോഡ് ചെയ്യുക</string>
<string name="upload_in_progress">അപ്‌ലോഡ് പുരോഗമിക്കുന്നു</string>
<string name="username">ഉപയോക്തൃനാമം</string> <string name="username">ഉപയോക്തൃനാമം</string>
<string name="password">രഹസ്യവാക്ക്</string> <string name="password">രഹസ്യവാക്ക്</string>
<string name="login_credential">താങ്കളുടെ കോമൺസ് ബീറ്റ അംഗത്വത്തിൽ പ്രവേശിക്കുക</string> <string name="login_credential">താങ്കളുടെ കോമൺസ് ബീറ്റ അംഗത്വത്തിൽ പ്രവേശിക്കുക</string>
@ -43,9 +64,13 @@
<string name="signup">അംഗത്വമെടുക്കുക</string> <string name="signup">അംഗത്വമെടുക്കുക</string>
<string name="logging_in_title">പ്രവേശിക്കുന്നു</string> <string name="logging_in_title">പ്രവേശിക്കുന്നു</string>
<string name="logging_in_message">ദയവായി കാത്തിരിക്കുക…</string> <string name="logging_in_message">ദയവായി കാത്തിരിക്കുക…</string>
<string name="login_success" fuzzy="true">പ്രവേശനം വിജയകരം!</string> <string name="updating_caption_title">അടിക്കുറിപ്പുകളും വിവരണങ്ങളും അപ്ഡേറ്റ് ചെയ്യുന്നു</string>
<string name="login_failed" fuzzy="true">പ്രവേശനം പരാജയപ്പെട്ടു!</string> <string name="updating_caption_message">ദയവായി കാത്തിരിക്കുക…</string>
<string name="login_success">പ്രവേശനം വിജയകരം!</string>
<string name="login_failed">പ്രവേശനം പരാജയപ്പെട്ടു!</string>
<string name="upload_failed">പ്രമാണം കണ്ടെത്താനായില്ല. ദയവായി മറ്റൊരു പ്രമാണം നോക്കുക.</string> <string name="upload_failed">പ്രമാണം കണ്ടെത്താനായില്ല. ദയവായി മറ്റൊരു പ്രമാണം നോക്കുക.</string>
<string name="retry_limit_reached">പരമാവധി വീണ്ടും ശ്രമിക്കാനുള്ള പരിധി എത്തി! അപ്‌ലോഡ് റദ്ദാക്കി വീണ്ടും ശ്രമിക്കുക</string>
<string name="unrestricted_battery_mode">ബാറ്ററി ഒപ്റ്റിമൈസേഷൻ ഓഫാക്കണോ?</string>
<string name="authentication_failed" fuzzy="true">സാധുതാനിർണ്ണയം പരാജയപ്പെട്ടു, ദയവായി വീണ്ടും പ്രവേശിക്കുക</string> <string name="authentication_failed" fuzzy="true">സാധുതാനിർണ്ണയം പരാജയപ്പെട്ടു, ദയവായി വീണ്ടും പ്രവേശിക്കുക</string>
<string name="uploading_started">അപ്‌ലോഡ് തുടങ്ങി!</string> <string name="uploading_started">അപ്‌ലോഡ് തുടങ്ങി!</string>
<string name="upload_completed_notification_title">%1$s അപ്‌ലോഡ് ചെയ്തിരിക്കുന്നു!</string> <string name="upload_completed_notification_title">%1$s അപ്‌ലോഡ് ചെയ്തിരിക്കുന്നു!</string>
@ -65,9 +90,14 @@
<string name="menu_from_camera">ചിത്രം എടുക്കുക</string> <string name="menu_from_camera">ചിത്രം എടുക്കുക</string>
<string name="menu_nearby">സമീപസ്ഥം</string> <string name="menu_nearby">സമീപസ്ഥം</string>
<string name="provider_contributions">എന്റെ അപ്‌ലോഡുകൾ</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_share">പങ്ക് വെയ്ക്കുക</string>
<string name="menu_view_file_page">പ്രമാണ താൾ കാണുക</string>
<string name="share_title_hint">അടിക്കുറിപ്പ് (നിർബന്ധം)</string> <string name="share_title_hint">അടിക്കുറിപ്പ് (നിർബന്ധം)</string>
<string name="add_caption_toast">ദയവായി ഈ ഫയലിന് ഒരു അടിക്കുറിപ്പ് നൽകുക</string>
<string name="share_description_hint">വിവരണം</string> <string name="share_description_hint">വിവരണം</string>
<string name="share_caption_hint">തലവാചകം</string>
<string name="login_failed_network" fuzzy="true">പ്രവേശിക്കാനായില്ല - നെറ്റ്‌വർക്ക് പരാജയപ്പെട്ടു</string> <string name="login_failed_network" fuzzy="true">പ്രവേശിക്കാനായില്ല - നെറ്റ്‌വർക്ക് പരാജയപ്പെട്ടു</string>
<string name="login_failed_throttled">നിരവധി വിജയകരമല്ലാത്ത ശ്രമങ്ങൾ നടന്നിരിക്കുന്നു. വീണ്ടും ശ്രമിക്കുന്നതിനു മുമ്പ് ഏതാനം മിനിറ്റുകൾ വിശ്രമിക്കുക.</string> <string name="login_failed_throttled">നിരവധി വിജയകരമല്ലാത്ത ശ്രമങ്ങൾ നടന്നിരിക്കുന്നു. വീണ്ടും ശ്രമിക്കുന്നതിനു മുമ്പ് ഏതാനം മിനിറ്റുകൾ വിശ്രമിക്കുക.</string>
<string name="login_failed_blocked">ക്ഷമിക്കുക, ഈ ഉപയോക്താവ് കോമൺസിൽ നിന്ന് തടയപ്പെട്ടിരിക്കുകയാണ്</string> <string name="login_failed_blocked">ക്ഷമിക്കുക, ഈ ഉപയോക്താവ് കോമൺസിൽ നിന്ന് തടയപ്പെട്ടിരിക്കുകയാണ്</string>
@ -103,6 +133,7 @@
<string name="menu_cancel_upload">റദ്ദാക്കുക</string> <string name="menu_cancel_upload">റദ്ദാക്കുക</string>
<string name="menu_download">ഡൗൺലോഡ്</string> <string name="menu_download">ഡൗൺലോഡ്</string>
<string name="preference_license">സ്വതേയുള്ള ഉപയോഗാനുമതി</string> <string name="preference_license">സ്വതേയുള്ള ഉപയോഗാനുമതി</string>
<string name="preference_theme">വിഷയം</string>
<string name="license_name_cc_by_sa_four">ആട്രിബ്യൂഷൻ-ഷെയർ‌എലൈക് 4.0</string> <string name="license_name_cc_by_sa_four">ആട്രിബ്യൂഷൻ-ഷെയർ‌എലൈക് 4.0</string>
<string name="license_name_cc_by_four">ആട്രിബ്യൂഷൻ 4.0</string> <string name="license_name_cc_by_four">ആട്രിബ്യൂഷൻ 4.0</string>
<string name="license_name_cc_by_sa">ആട്രിബ്യൂഷൻ-ഷെയർ‌എലൈക് 3.0</string> <string name="license_name_cc_by_sa">ആട്രിബ്യൂഷൻ-ഷെയർ‌എലൈക് 3.0</string>
@ -122,6 +153,7 @@
<string name="detail_panel_cats_label">വർഗ്ഗങ്ങൾ</string> <string name="detail_panel_cats_label">വർഗ്ഗങ്ങൾ</string>
<string name="detail_panel_cats_loading">ശേഖരിക്കുന്നു…</string> <string name="detail_panel_cats_loading">ശേഖരിക്കുന്നു…</string>
<string name="detail_panel_cats_none">ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല</string> <string name="detail_panel_cats_none">ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല</string>
<string name="detail_caption_empty">അടിക്കുറിപ്പില്ല</string>
<string name="detail_description_empty">വിവരണമൊന്നുമില്ല</string> <string name="detail_description_empty">വിവരണമൊന്നുമില്ല</string>
<string name="detail_discussion_empty">സംവാദങ്ങളില്ല</string> <string name="detail_discussion_empty">സംവാദങ്ങളില്ല</string>
<string name="detail_license_empty">അജ്ഞാതമായ അനുമതി</string> <string name="detail_license_empty">അജ്ഞാതമായ അനുമതി</string>

View file

@ -13,6 +13,7 @@
* Matma Rex * Matma Rex
* Mazab IZW * Mazab IZW
* Olaf * Olaf
* PanWor
* Rail * Rail
* Railfail536 * Railfail536
* Rainbow P * Rainbow P
@ -137,6 +138,8 @@
<string name="menu_from_camera">Zrób zdjęcie</string> <string name="menu_from_camera">Zrób zdjęcie</string>
<string name="menu_nearby">W pobliżu</string> <string name="menu_nearby">W pobliżu</string>
<string name="provider_contributions">Wysłane przeze mnie pliki</string> <string name="provider_contributions">Wysłane przeze mnie pliki</string>
<string name="menu_copy_link">Skopiuj link</string>
<string name="menu_link_copied">Link został skopiowany do schowka</string>
<string name="menu_share">Udostępnij</string> <string name="menu_share">Udostępnij</string>
<string name="menu_view_file_page">Pokaż stronę pliku</string> <string name="menu_view_file_page">Pokaż stronę pliku</string>
<string name="share_title_hint">Podpis (wymagany)</string> <string name="share_title_hint">Podpis (wymagany)</string>
@ -155,6 +158,7 @@
<string name="categories_search_text_hint">Szukaj kategorii</string> <string name="categories_search_text_hint">Szukaj kategorii</string>
<string name="depicts_search_text_hint">Wyszukiwanie elementów, które przedstawiają twoje media (góra, Tadż Mahal itp.)</string> <string name="depicts_search_text_hint">Wyszukiwanie elementów, które przedstawiają twoje media (góra, Tadż Mahal itp.)</string>
<string name="menu_save_categories">Zapisz</string> <string name="menu_save_categories">Zapisz</string>
<string name="menu_overflow_desc">Rozszerzone menu</string>
<string name="refresh_button">Odśwież</string> <string name="refresh_button">Odśwież</string>
<string name="display_list_button">Lista</string> <string name="display_list_button">Lista</string>
<string name="contributions_subtitle_zero">(Nie ma jeszcze przesłanych plików)</string> <string name="contributions_subtitle_zero">(Nie ma jeszcze przesłanych plików)</string>
@ -311,6 +315,7 @@
<string name="copy_wikicode">Skopiuj wikitext do schowka</string> <string name="copy_wikicode">Skopiuj wikitext do schowka</string>
<string name="wikicode_copied">Wikitext został skopiowany do schowka</string> <string name="wikicode_copied">Wikitext został skopiowany do schowka</string>
<string name="nearby_location_not_available">W pobliżu może nie działać poprawnie, Lokalizacja jest niedostępna.</string> <string name="nearby_location_not_available">W pobliżu może nie działać poprawnie, Lokalizacja jest niedostępna.</string>
<string name="nearby_showing_pins_offline">Brak połączenia z internetem. Wyświetlane są tylko miejsca z pamięci podręcznej.</string>
<string name="upload_location_access_denied">Odmowa dostępu do lokalizacji. Aby skorzystać z tej funkcji, ustaw swoją lokalizację ręcznie.</string> <string name="upload_location_access_denied">Odmowa dostępu do lokalizacji. Aby skorzystać z tej funkcji, ustaw swoją lokalizację ręcznie.</string>
<string name="location_permission_rationale_nearby">Uprawnienie wymagane do wyświetlania listy pobliskich miejsc</string> <string name="location_permission_rationale_nearby">Uprawnienie wymagane do wyświetlania listy pobliskich miejsc</string>
<string name="location_permission_rationale_explore">Uprawnienie wymagane do wyświetlania listy pobliskich zdjęć</string> <string name="location_permission_rationale_explore">Uprawnienie wymagane do wyświetlania listy pobliskich zdjęć</string>
@ -394,11 +399,13 @@
<string name="delete">Usuń</string> <string name="delete">Usuń</string>
<string name="Achievements">Osiągnięcia</string> <string name="Achievements">Osiągnięcia</string>
<string name="Profile">Profil</string> <string name="Profile">Profil</string>
<string name="badges">Odznaki</string>
<string name="statistics">Statystyki</string> <string name="statistics">Statystyki</string>
<string name="statistics_thanks">Otrzymane Dzięki</string> <string name="statistics_thanks">Otrzymane Dzięki</string>
<string name="statistics_featured">Wyróżnione ilustracje</string> <string name="statistics_featured">Wyróżnione ilustracje</string>
<string name="statistics_wikidata_edits">Obrazy za pośrednictwem \"Pobliskie miejsca\"</string> <string name="statistics_wikidata_edits">Obrazy za pośrednictwem \"Pobliskie miejsca\"</string>
<string name="level" fuzzy="true">Poziom</string> <string name="level">Poziom %d</string>
<string name="profileLevel">%s (Poziom %s)</string>
<string name="images_uploaded">Przesłane obrazy</string> <string name="images_uploaded">Przesłane obrazy</string>
<string name="image_reverts">Nie wycofane obrazy</string> <string name="image_reverts">Nie wycofane obrazy</string>
<string name="images_used_by_wiki">Wykorzystane obrazy</string> <string name="images_used_by_wiki">Wykorzystane obrazy</string>
@ -465,6 +472,8 @@
<string name="in_app_camera_location_access_explanation">Zezwól aplikacji na pobieranie lokalizacji, jeśli kamera jej nie rejestruje. Niektóre kamery urządzeń nie rejestrują lokalizacji. W takich przypadkach pozwolenie aplikacji na pobieranie i dołączanie do niej lokalizacji sprawia, że Twój wkład jest bardziej użyteczny. Możesz to zmienić w dowolnym momencie w Ustawieniach</string> <string name="in_app_camera_location_access_explanation">Zezwól aplikacji na pobieranie lokalizacji, jeśli kamera jej nie rejestruje. Niektóre kamery urządzeń nie rejestrują lokalizacji. W takich przypadkach pozwolenie aplikacji na pobieranie i dołączanie do niej lokalizacji sprawia, że Twój wkład jest bardziej użyteczny. Możesz to zmienić w dowolnym momencie w Ustawieniach</string>
<string name="option_allow">Zezwól</string> <string name="option_allow">Zezwól</string>
<string name="option_dismiss">Odrzuć</string> <string name="option_dismiss">Odrzuć</string>
<string name="in_app_camera_needs_location">Zezwól na dostęp do lokalizacji w Ustawieniach, a następnie spróbuj ponownie.\n\nUwaga: Jeśli aplikacja nie będzie w stanie uzyskać danych o lokalizacji z urządzenia w krótkim czasie, przesłany plik może nie zawierać tych informacji.</string>
<string name="in_app_camera_location_permission_rationale">Aparat w aplikacji potrzebuje uprawnień na dostęp do lokalizacji, aby dołączyć ją do zdjęć, jeśli nie jest dostępna w danych EXIF. Zezwól aplikacji na dostęp do lokalizacji, a następnie spróbuj ponownie.\n\nUwaga: Jeśli aplikacja nie będzie w stanie uzyskać danych o lokalizacji z urządzenia w krótkim czasie, przesłany plik może nie zawierać tych informacji.</string>
<string name="location_loss_warning" fuzzy="true">Upewnij się, że ten nowy selektor Androida nie usuwa lokalizacji ze zdjęć.</string> <string name="location_loss_warning" fuzzy="true">Upewnij się, że ten nowy selektor Androida nie usuwa lokalizacji ze zdjęć.</string>
<string name="nearby_campaign_dismiss_message">Kampanie już nie będą widoczne. Jednak w razie potrzeby możesz ponownie włączyć to powiadomienie w ustawieniach.</string> <string name="nearby_campaign_dismiss_message">Kampanie już nie będą widoczne. Jednak w razie potrzeby możesz ponownie włączyć to powiadomienie w ustawieniach.</string>
<string name="this_function_needs_network_connection" fuzzy="true">Ta funkcja wymaga połączenia sieciowego, sprawdź ustawienia połączenia.</string> <string name="this_function_needs_network_connection" fuzzy="true">Ta funkcja wymaga połączenia sieciowego, sprawdź ustawienia połączenia.</string>
@ -781,4 +790,6 @@
<string name="do_you_want_to_open_kml_file">Chcesz otworzyć plik KML?</string> <string name="do_you_want_to_open_kml_file">Chcesz otworzyć plik KML?</string>
<string name="failed_to_save_kml_file">Nie udało się zapisać pliku KML.</string> <string name="failed_to_save_kml_file">Nie udało się zapisać pliku KML.</string>
<string name="failed_to_save_gpx_file">Nie udało się zapisać pliku GPX.</string> <string name="failed_to_save_gpx_file">Nie udało się zapisać pliku GPX.</string>
<string name="talk">Dyskusja</string>
<string name="custom_selector_delete">Usuń</string>
</resources> </resources>

View file

@ -813,4 +813,7 @@
<string name="account_vanish_request_confirm">L\'eliminassion a l\'é &lt;b&gt;l\'ùltima arsorsa&lt;/b&gt; e a dovrìa &lt;b&gt;esse dovrà mach si chiel a veul chité ëd modifiché për sempe&lt;/b&gt; e ëdcò s\'a veul ëstërmé pi che possìbil soe assossiassion passà.&lt;br/&gt;&lt;br/&gt;La dëscancelassion ëd cont su Wikimedia a l\'é fàita an modificand sò stranòm an manera che j\'àutri a peulo pa arconòsse soe contribussion ant un process ciamà dëscancelassion ëd cont. &lt;b&gt;La sparission a garantiss pa l\'anonimà complet ni a gava le contribussion dai proget&lt;/b&gt;.</string> <string name="account_vanish_request_confirm">L\'eliminassion a l\'é &lt;b&gt;l\'ùltima arsorsa&lt;/b&gt; e a dovrìa &lt;b&gt;esse dovrà mach si chiel a veul chité ëd modifiché për sempe&lt;/b&gt; e ëdcò s\'a veul ëstërmé pi che possìbil soe assossiassion passà.&lt;br/&gt;&lt;br/&gt;La dëscancelassion ëd cont su Wikimedia a l\'é fàita an modificand sò stranòm an manera che j\'àutri a peulo pa arconòsse soe contribussion ant un process ciamà dëscancelassion ëd cont. &lt;b&gt;La sparission a garantiss pa l\'anonimà complet ni a gava le contribussion dai proget&lt;/b&gt;.</string>
<string name="caption">Legenda</string> <string name="caption">Legenda</string>
<string name="caption_copied_to_clipboard">Legenda copià an sla taulëtta</string> <string name="caption_copied_to_clipboard">Legenda copià an sla taulëtta</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Congratulassion, tute le fòto ëd s\'àlbom a son ëstàita carià opura marcà coma da nen carié.</string>
<string name="show_in_explore">Smon-e andrinta a Explore</string>
<string name="show_in_nearby">Smon-e andrinta a Nearby</string>
</resources> </resources>

View file

@ -15,7 +15,7 @@
<string name="share_license_summary" fuzzy="true">دا انځور به د %1$s په منښتليک سمبال وي.</string> <string name="share_license_summary" fuzzy="true">دا انځور به د %1$s په منښتليک سمبال وي.</string>
<string name="app_name">ويکي خونديځ</string> <string name="app_name">ويکي خونديځ</string>
<string name="menu_settings">امستنې</string> <string name="menu_settings">امستنې</string>
<string name="username">کارن-نوم</string> <string name="username">کارننوم</string>
<string name="password">پټنوم</string> <string name="password">پټنوم</string>
<string name="login">ننوتل</string> <string name="login">ننوتل</string>
<string name="signup">نومليکنه</string> <string name="signup">نومليکنه</string>
@ -88,4 +88,5 @@
<string name="navigation_item_settings">امستنې</string> <string name="navigation_item_settings">امستنې</string>
<string name="navigation_item_feedback">غبرگون</string> <string name="navigation_item_feedback">غبرگون</string>
<string name="navigation_item_logout">وتل</string> <string name="navigation_item_logout">وتل</string>
<string name="account">گڼون</string>
</resources> </resources>

View file

@ -333,6 +333,7 @@
<string name="copy_wikicode">Копирование викикода в буфер обмена</string> <string name="copy_wikicode">Копирование викикода в буфер обмена</string>
<string name="wikicode_copied">Викикод скопирован в буфер обмена</string> <string name="wikicode_copied">Викикод скопирован в буфер обмена</string>
<string name="nearby_location_not_available">Функция «Поблизости» может работать некорректно, определение местоположения недоступно.</string> <string name="nearby_location_not_available">Функция «Поблизости» может работать некорректно, определение местоположения недоступно.</string>
<string name="nearby_showing_pins_offline">Интернет недоступен. Показаны только кэшированные места.</string>
<string name="upload_location_access_denied">Доступ к местоположению запрещён. Чтобы использовать эту функцию, укажите своё местоположение вручную.</string> <string name="upload_location_access_denied">Доступ к местоположению запрещён. Чтобы использовать эту функцию, укажите своё местоположение вручную.</string>
<string name="location_permission_rationale_nearby">Необходимо разрешение для отображения списка мест поблизости</string> <string name="location_permission_rationale_nearby">Необходимо разрешение для отображения списка мест поблизости</string>
<string name="location_permission_rationale_explore">Необходимо разрешение для отображения списка мест поблизости</string> <string name="location_permission_rationale_explore">Необходимо разрешение для отображения списка мест поблизости</string>
@ -869,7 +870,9 @@
<string name="usages_on_commons_heading">Викисклад</string> <string name="usages_on_commons_heading">Викисклад</string>
<string name="usages_on_other_wikis_heading">Другие вики</string> <string name="usages_on_other_wikis_heading">Другие вики</string>
<string name="file_usages_container_heading">Использование файла</string> <string name="file_usages_container_heading">Использование файла</string>
<string name="title_activity_single_web_view">SingleWebViewActivity</string>
<string name="account">Учётная запись</string> <string name="account">Учётная запись</string>
<string name="caption">Подпись</string> <string name="caption">Подпись</string>
<string name="caption_copied_to_clipboard">Подпись скопирована в буфер обмена</string> <string name="caption_copied_to_clipboard">Подпись скопирована в буфер обмена</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Поздравляем, все фотографии в этом альбоме либо загружены, либо помечены как не предназначенные для загрузки.</string>
</resources> </resources>

View file

@ -820,4 +820,5 @@
<string name="account_vanish_request_confirm">Att få kontot att försvinna är en &lt;b&gt;sista utväg&lt;/b&gt; och bör &lt;b&gt;endast användas när du vill sluta redigera för alltid&lt;/b&gt; och även dölja så många av dina tidigare associationer som möjligt.&lt;br/&gt;&lt;br/&gt;Konton raderas på Wikimedia Commons genom att ändra kontonamnet för att göra så att andra inte kan känna igen bidragen i en process som kallas kontoförsvinnande. &lt;b&gt;Försvinnande garanterar inte fullständig anonymitet eller att bidrag tas bort från projekten&lt;/b&gt;.</string> <string name="account_vanish_request_confirm">Att få kontot att försvinna är en &lt;b&gt;sista utväg&lt;/b&gt; och bör &lt;b&gt;endast användas när du vill sluta redigera för alltid&lt;/b&gt; och även dölja så många av dina tidigare associationer som möjligt.&lt;br/&gt;&lt;br/&gt;Konton raderas på Wikimedia Commons genom att ändra kontonamnet för att göra så att andra inte kan känna igen bidragen i en process som kallas kontoförsvinnande. &lt;b&gt;Försvinnande garanterar inte fullständig anonymitet eller att bidrag tas bort från projekten&lt;/b&gt;.</string>
<string name="caption">Bildtext</string> <string name="caption">Bildtext</string>
<string name="caption_copied_to_clipboard">Bildtext kopierades till urklipp</string> <string name="caption_copied_to_clipboard">Bildtext kopierades till urklipp</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Grattis! Alla bilder i detta album har antingen laddats upp eller markerats för att inte laddas upp.</string>
</resources> </resources>

View file

@ -48,6 +48,7 @@
* Yfdyh000 * Yfdyh000
* Zhang8569 * Zhang8569
* ZhaoGang * ZhaoGang
* 七八年再来一次
* 予弦 * 予弦
* 佛壁灯 * 佛壁灯
* 列维劳德 * 列维劳德
@ -61,7 +62,7 @@
<resources> <resources>
<string name="commons_facebook">共享资源Facebook页面</string> <string name="commons_facebook">共享资源Facebook页面</string>
<string name="commons_github">共享资源Github源代码</string> <string name="commons_github">共享资源Github源代码</string>
<string name="commons_logo">共享资源标</string> <string name="commons_logo">共享资源标</string>
<string name="commons_website">共享资源网站</string> <string name="commons_website">共享资源网站</string>
<string name="exit_location_picker">退出位置选择器</string> <string name="exit_location_picker">退出位置选择器</string>
<string name="submit">提交</string> <string name="submit">提交</string>
@ -345,7 +346,7 @@
<string name="welcome_skip_button">跳过教程</string> <string name="welcome_skip_button">跳过教程</string>
<string name="no_internet">互联网不可用</string> <string name="no_internet">互联网不可用</string>
<string name="error_notifications">检索通知时出错</string> <string name="error_notifications">检索通知时出错</string>
<string name="error_review">获取审查图片错误。按刷新键重试。</string> <string name="error_review">获取审查图片错误。按刷新键重试。</string>
<string name="no_notifications">找不到通知</string> <string name="no_notifications">找不到通知</string>
<string name="about_translate">翻译</string> <string name="about_translate">翻译</string>
<string name="about_translate_title">语言</string> <string name="about_translate_title">语言</string>
@ -378,13 +379,13 @@
<string name="explore_tab_title_mobile">通过移动端上传</string> <string name="explore_tab_title_mobile">通过移动端上传</string>
<string name="explore_tab_title_map">地图</string> <string name="explore_tab_title_map">地图</string>
<string name="successful_wikidata_edit">图片已添加到维基数据上的%1$s</string> <string name="successful_wikidata_edit">图片已添加到维基数据上的%1$s</string>
<string name="wikidata_edit_failure">更新对应维基数据实体失败!</string> <string name="wikidata_edit_failure">更新对应维基数据项目失败!</string>
<string name="menu_set_wallpaper">设为壁纸</string> <string name="menu_set_wallpaper">设为壁纸</string>
<string name="wallpaper_set_successfully">壁纸已成功设置!</string> <string name="wallpaper_set_successfully">壁纸已成功设置!</string>
<string name="quiz">测验</string> <string name="quiz">测验</string>
<string name="quiz_question_string">这个图片可以上传吗?</string> <string name="quiz_question_string">这个图片可以上传吗?</string>
<string name="question">问题</string> <string name="question">问题</string>
<string name="result">成绩</string> <string name="result">结果</string>
<string name="quiz_back_button">如果您继续上传需要删除的图片,您的帐户可能会被封禁。你确定要结束测验吗?</string> <string name="quiz_back_button">如果您继续上传需要删除的图片,您的帐户可能会被封禁。你确定要结束测验吗?</string>
<string name="quiz_alert_message">您上传的图片超过%1$s已被删除。如果您继续上传需要删除的图片您的帐户可能会被封禁。\n\n您是否希望再次查看该教程然后进行测验以帮助您了解应该或不应上传的图像类型</string> <string name="quiz_alert_message">您上传的图片超过%1$s已被删除。如果您继续上传需要删除的图片您的帐户可能会被封禁。\n\n您是否希望再次查看该教程然后进行测验以帮助您了解应该或不应上传的图像类型</string>
<string name="selfie_answer">自拍没有太多的百科全书价值。除非您已经有关于您的维基百科文章,否则请不要上传自己的照片。</string> <string name="selfie_answer">自拍没有太多的百科全书价值。除非您已经有关于您的维基百科文章,否则请不要上传自己的照片。</string>
@ -467,7 +468,7 @@
<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_uploads">欢迎使用共享资源!\n\n通过点击添加按钮以上传您的首个媒体。</string>
<string name="no_categories_selected">未提交分类</string> <string name="no_categories_selected">未提交分类</string>
<string name="no_categories_selected_warning_desc">没有类别的图像很少可用。确实要继续而不选择类别吗?</string> <string name="no_categories_selected_warning_desc">不带分类的图片很难有机会被利用到,您确定您要不选择分类来继续吗?</string>
<string name="no_depictions_selected">没有选择描写</string> <string name="no_depictions_selected">没有选择描写</string>
<string name="no_depictions_selected_warning_desc">带有描述的图像更容易被发现并且更可能被使用。您确定不选择描述继续吗?</string> <string name="no_depictions_selected_warning_desc">带有描述的图像更容易被发现并且更可能被使用。您确定不选择描述继续吗?</string>
<string name="back_button_warning">取消上传</string> <string name="back_button_warning">取消上传</string>
@ -477,12 +478,12 @@
<string name="search_this_area">搜索这个区域</string> <string name="search_this_area">搜索这个区域</string>
<string name="nearby_card_permission_title">权限申请</string> <string name="nearby_card_permission_title">权限申请</string>
<string name="nearby_card_permission_explanation">您希望我们获取您当前的位置来显示最近的需要图片的地方吗?</string> <string name="nearby_card_permission_explanation">您希望我们获取您当前的位置来显示最近的需要图片的地方吗?</string>
<string name="unable_to_display_nearest_place">无法显示没有位置权限的需要图片的最近位置</string> <string name="unable_to_display_nearest_place">没有位置权限,无法显示需要图片的最近地点</string>
<string name="never_ask_again">不再询问</string> <string name="never_ask_again">不再询问</string>
<string name="display_location_permission_title">申请位置权限</string> <string name="display_location_permission_title">申请位置权限</string>
<string name="display_location_permission_explanation">申请附近通知卡查看功能所必需的位置权限。</string> <string name="display_location_permission_explanation">申请附近通知卡查看功能所必需的位置权限。</string>
<string name="achievements_fetch_failed">出了点问题,我们无法获取成就</string> <string name="achievements_fetch_failed">出了点问题,我们无法获取成就</string>
<string name="achievements_fetch_failed_ultimate_achievement">您做出了许多贡献,我们的计算系统无法应对。这是最终的成就</string> <string name="achievements_fetch_failed_ultimate_achievement">您做出的贡献多到让我们的成就计算系统无法应对,此为最终成果</string>
<string name="ends_on">结束时间:</string> <string name="ends_on">结束时间:</string>
<string name="display_campaigns">显示活动</string> <string name="display_campaigns">显示活动</string>
<string name="display_campaigns_explanation">显示正在进行的活动</string> <string name="display_campaigns_explanation">显示正在进行的活动</string>
@ -558,7 +559,7 @@
<string name="exif_tag_name_lensModel">镜头型号</string> <string name="exif_tag_name_lensModel">镜头型号</string>
<string name="exif_tag_name_serialNumbers">序列号</string> <string name="exif_tag_name_serialNumbers">序列号</string>
<string name="exif_tag_name_software">软件</string> <string name="exif_tag_name_software">软件</string>
<string name="media_location_permission_denied">拒绝访问媒体位置</string> <string name="media_location_permission_denied">拒绝访问媒体位置</string>
<string name="add_location_manually">我们可能无法自动从你上传的图片中获取位置数据。提交前请为每张图片添加适当的位置</string> <string name="add_location_manually">我们可能无法自动从你上传的图片中获取位置数据。提交前请为每张图片添加适当的位置</string>
<string name="share_text">直接在您手机上的维基共享资源应用中上传照片。立即下载共享资源应用:%1$s</string> <string name="share_text">直接在您手机上的维基共享资源应用中上传照片。立即下载共享资源应用:%1$s</string>
<string name="share_via">分享到...</string> <string name="share_via">分享到...</string>
@ -799,7 +800,7 @@
<string name="full_screen_mode_zoom_info">用两根手指放大和缩小。</string> <string name="full_screen_mode_zoom_info">用两根手指放大和缩小。</string>
<string name="full_screen_mode_features_info">快速长距离滑动来执行以下操作:\n- 向左/右:前往上一个/下一个\n- 向上:选择\n- 向下:标记为不上传</string> <string name="full_screen_mode_features_info">快速长距离滑动来执行以下操作:\n- 向左/右:前往上一个/下一个\n- 向上:选择\n- 向下:标记为不上传</string>
<string name="set_up_avatar_toast_string">要设置你的排行榜头像,请点击任意图片上三点式菜单中的\"设置为头像\"</string> <string name="set_up_avatar_toast_string">要设置你的排行榜头像,请点击任意图片上三点式菜单中的\"设置为头像\"</string>
<string name="similar_coordinate_description_auto_set">图片坐标并不是准确的坐标,但上传这张图片的人认为它们足够接近</string> <string name="similar_coordinate_description_auto_set">坐标虽不准确,但上传这张图片的人认为已经足够接近了</string>
<string name="storage_permissions_denied">存储权限被拒绝</string> <string name="storage_permissions_denied">存储权限被拒绝</string>
<string name="unable_to_share_upload_item">无法分享此项目</string> <string name="unable_to_share_upload_item">无法分享此项目</string>
<string name="permissions_are_required_for_functionality">功能需要权限</string> <string name="permissions_are_required_for_functionality">功能需要权限</string>
@ -828,7 +829,7 @@
<item quantity="one">已选择%d个图像</item> <item quantity="one">已选择%d个图像</item>
<item quantity="other">已选择%d个图像</item> <item quantity="other">已选择%d个图像</item>
</plurals> </plurals>
<string name="multiple_files_depiction">请记住,每次多图片上传会为其中的所有图片标注相同的分类和描述。如果这些图片并不共享同样的描述和分类,请分别进行多次上传。</string> <string name="multiple_files_depiction">请记住,多重上传中的所有图片都会标注相同的分类和描述。如果这些图片并不共享同样的描述和分类,请分别多次单独进行上传。</string>
<string name="multiple_files_depiction_header">关于多图片上传的提醒</string> <string name="multiple_files_depiction_header">关于多图片上传的提醒</string>
<string name="nearby_wikitalk">向维基数据报告关于该项的问题</string> <string name="nearby_wikitalk">向维基数据报告关于该项的问题</string>
<string name="please_enter_some_comments">请输入一些评论。</string> <string name="please_enter_some_comments">请输入一些评论。</string>
@ -857,7 +858,16 @@
<string name="green_pin">这个地点已经有照片了。</string> <string name="green_pin">这个地点已经有照片了。</string>
<string name="grey_pin">现在检查这个地点是否有照片。</string> <string name="grey_pin">现在检查这个地点是否有照片。</string>
<string name="error_while_loading">加载时出错</string> <string name="error_while_loading">加载时出错</string>
<string name="no_usages_found">未找到用法</string>
<string name="usages_on_commons_heading">维基共享资源</string> <string name="usages_on_commons_heading">维基共享资源</string>
<string name="usages_on_other_wikis_heading">其它wiki</string> <string name="usages_on_other_wikis_heading">其它wiki</string>
<string name="file_usages_container_heading">文件用途</string> <string name="file_usages_container_heading">文件用途</string>
<string name="title_activity_single_web_view">单一网页视图活动</string>
<string name="account">账号</string>
<string name="vanish_account">隐退账号</string>
<string name="account_vanish_request_confirm_title">隐退账号警告</string>
<string name="account_vanish_request_confirm">隐退是一个&lt;b&gt;最后的手段&lt;/b&gt;,应该&lt;b&gt;仅在您希望永远停止编辑&lt;/b&gt;,并尽可能隐藏您过去的关联时使用。&lt;br/&gt;&lt;br/&gt;在维基媒体共享资源上删除账户是通过更改您的账户名称,使其他人无法识别您的贡献,这个过程称为账户隐退。&lt;b&gt;隐退并不能保证完全匿名,也无法删除对项目的贡献&lt;/b&gt;</string>
<string name="caption">说明</string>
<string name="caption_copied_to_clipboard">已复制到剪贴板</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">恭喜,专辑中的所有图片都已上传或标记为不上传。</string>
</resources> </resources>

View file

@ -867,5 +867,8 @@ Upload your first media by tapping on the add button.</string>
<string name="account_vanish_request_confirm"><![CDATA[Vanishing is a <b>last resort</b> and should <b>only be used when you wish to stop editing forever</b> and also to hide as many of your past associations as possible.<br/><br/>Account deletion on Wikimedia Commons is done by changing your account name to make it so others cannot recognize your contributions in a process called account vanishing. <b>Vanishing does not guarantee complete anonymity or remove contributions to the projects</b>.]]></string> <string name="account_vanish_request_confirm"><![CDATA[Vanishing is a <b>last resort</b> and should <b>only be used when you wish to stop editing forever</b> and also to hide as many of your past associations as possible.<br/><br/>Account deletion on Wikimedia Commons is done by changing your account name to make it so others cannot recognize your contributions in a process called account vanishing. <b>Vanishing does not guarantee complete anonymity or remove contributions to the projects</b>.]]></string>
<string name="caption">Caption</string> <string name="caption">Caption</string>
<string name="caption_copied_to_clipboard">Caption copied to clipboard</string> <string name="caption_copied_to_clipboard">Caption copied to clipboard</string>
<string name="congratulations_all_pictures_in_this_album_have_been_either_uploaded_or_marked_as_not_for_upload">Congratulations, all pictures in this album have been either uploaded or marked as not for upload.</string>
<string name="show_in_explore">Show in Explore</string>
<string name="show_in_nearby">Show in Nearby</string>
</resources> </resources>

View file

@ -11,6 +11,7 @@ import fr.free.nrw.commons.upload.GpsCategoryModel
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.BehaviorSubject
import media import media
import org.junit.Assert
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers
@ -331,4 +332,42 @@ class CategoriesModelTest {
media(), media(),
) )
} }
@Test
fun `test valid input with XXXX in it between the expected range 20XX`() {
val input = categoriesModel.isSpammyCategory("Amavenita (ship, 2014)")
Assert.assertFalse(input)
}
@Test
fun `test valid input with XXXXs in it between the expected range 20XXs`() {
val input = categoriesModel.isSpammyCategory("Amavenita (ship, 2014s)")
Assert.assertFalse(input)
}
@Test
fun `test invalid category when have needing in the input`() {
val input = categoriesModel.isSpammyCategory("Media needing categories as of 30 March 2017")
Assert.assertTrue(input)
}
@Test
fun `test invalid category when have taken on in the input`() {
val input = categoriesModel.isSpammyCategory("Photographs taken on 2015-12-08")
Assert.assertTrue(input)
}
@Test
fun `test invalid category when have yy mm or yy mm dd in the input`() {
// filtering based on [., /, -] separators between the dates.
val input = categoriesModel.isSpammyCategory("Image class 09.14")
Assert.assertTrue(input)
}
@Test
fun `test invalid category when have years not in 20XX range`() {
val input = categoriesModel.isSpammyCategory("Japan in the 1400s")
Assert.assertTrue(input)
}
} }

View file

@ -11,16 +11,19 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import androidx.test.core.app.ApplicationProvider import androidx.test.core.app.ApplicationProvider
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.nhaarman.mockitokotlin2.eq
import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.OkHttpConnectionFactory
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.TestCommonsApplication
import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.contributions.MainActivity
import fr.free.nrw.commons.createTestClient import fr.free.nrw.commons.createTestClient
import org.junit.Assert import org.junit.Assert
import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Ignore import org.junit.Ignore
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` import org.mockito.Mockito.`when`
@ -34,6 +37,7 @@ import org.robolectric.annotation.LooperMode
import org.robolectric.fakes.RoboMenu import org.robolectric.fakes.RoboMenu
import org.robolectric.fakes.RoboMenuItem import org.robolectric.fakes.RoboMenuItem
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class) @Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED) @LooperMode(LooperMode.Mode.PAUSED)
@ -151,6 +155,14 @@ class ExploreFragmentUnitTest {
Shadows.shadowOf(getMainLooper()).idle() Shadows.shadowOf(getMainLooper()).idle()
val menu: Menu = RoboMenu(context) val menu: Menu = RoboMenu(context)
fragment.onCreateOptionsMenu(menu, inflater) fragment.onCreateOptionsMenu(menu, inflater)
verify(inflater).inflate(R.menu.menu_search, menu)
val captor = ArgumentCaptor.forClass(
Int::class.java
)
verify(inflater).inflate(captor.capture(), eq(menu))
val capturedLayout = captor.value
assertTrue(capturedLayout == R.menu.menu_search || capturedLayout == R.menu.explore_fragment_menu)
} }
} }