Merge branch 'main' into issue-5872-fix-search-error-examined

This commit is contained in:
Nicolas Raoul 2024-11-13 21:57:53 +09:00 committed by GitHub
commit 5b114955e4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
126 changed files with 1601 additions and 1201 deletions

View file

@ -1,16 +1,12 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintNewerVersionAvailable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClassWithOnlyPrivateConstructors" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ConfusingElse" enabled="true" level="WARNING" enabled_by_default="true">
<option name="reportWhenNoStatementFollow" value="true" />
</inspection_tool>
<inspection_tool class="ControlFlowStatementWithoutBraces" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="DefaultNotLastCaseInSwitch" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ExplicitThis" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="FieldMayBeFinal" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LocalCanBeFinal" enabled="true" level="WARNING" enabled_by_default="true">
<option name="REPORT_VARIABLES" value="true" />
<option name="REPORT_PARAMETERS" value="true" />
@ -25,13 +21,11 @@
<option name="ignoreInMatchingInstanceof" value="false" />
</inspection_tool>
<inspection_tool class="ProblematicWhitespace" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ProtectedMemberInFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantFieldInitialization" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="RedundantImplements" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreSerializable" value="false" />
<option name="ignoreCloneable" value="false" />
</inspection_tool>
<inspection_tool class="RedundantMethodOverride" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SimplifiableEqualsExpression" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TypeParameterExtendsFinalClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarilyQualifiedStaticUsage" enabled="true" level="WARNING" enabled_by_default="true">
@ -47,6 +41,5 @@
<inspection_tool class="UnnecessaryQualifierForThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessarySuperConstructor" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryThis" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnnecessaryToStringCall" enabled="true" level="WARNING" enabled_by_default="true" />
</profile>
</component>

View file

@ -93,6 +93,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
//Mocking
testImplementation("io.mockk:mockk:1.13.4")
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
testImplementation 'org.mockito:mockito-inline:5.2.0'
testImplementation 'org.mockito:mockito-core:5.6.0'
@ -226,7 +227,7 @@ android {
excludes += ['META-INF/androidx.*']
}
resources {
excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro']
excludes += ['META-INF/androidx.*', 'META-INF/proguard/androidx-annotations.pro', '/META-INF/LICENSE.md', '/META-INF/LICENSE-notice.md']
}
}
@ -380,7 +381,7 @@ android {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.3.2'
kotlinCompilerExtensionVersion '1.5.8'
}
namespace 'fr.free.nrw.commons'
lint {

View file

@ -105,7 +105,7 @@ class AboutActivityTest {
fun testLaunchTranslate() {
Espresso.onView(ViewMatchers.withId(R.id.about_translate)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(android.R.id.button1)).perform(ViewActions.click())
val langCode = CommonsApplication.getInstance().languageLookUpTable.codes[0]
val langCode = CommonsApplication.instance.languageLookUpTable!!.codes[0]
Intents.intended(
CoreMatchers.allOf(
IntentMatchers.hasAction(Intent.ACTION_VIEW),

View file

@ -99,7 +99,6 @@
android:exported="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize">
<intent-filter android:label="@string/intent_share_upload_label">
<action android:name="android.intent.action.SEND" />
@ -122,7 +121,7 @@
android:name=".contributions.MainActivity"
android:configChanges="screenSize|keyboard|orientation"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" />
/>
<activity
android:name=".settings.SettingsActivity"
android:label="@string/title_activity_settings" />

View file

@ -46,7 +46,7 @@ class BaseMarker {
val drawable: Drawable = context.resources.getDrawable(drawableResId)
icon =
if (drawable is BitmapDrawable) {
(drawable as BitmapDrawable).bitmap
drawable.bitmap
} else {
val bitmap =
Bitmap.createBitmap(

View file

@ -1,433 +0,0 @@
package fr.free.nrw.commons;
import static fr.free.nrw.commons.data.DBOpenHelper.CONTRIBUTIONS_TABLE;
import static org.acra.ReportField.ANDROID_VERSION;
import static org.acra.ReportField.APP_VERSION_CODE;
import static org.acra.ReportField.APP_VERSION_NAME;
import static org.acra.ReportField.PHONE_MODEL;
import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_COMMENT;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.multidex.MultiDexApplication;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao.Table;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.category.CategoryDao;
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler;
import fr.free.nrw.commons.concurrency.ThreadPoolService;
import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.language.AppLanguageLookUpTable;
import fr.free.nrw.commons.logging.FileLoggingTree;
import fr.free.nrw.commons.logging.LogUtils;
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher;
import fr.free.nrw.commons.settings.Prefs;
import fr.free.nrw.commons.upload.FileUtils;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.internal.functions.Functions;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import org.acra.ACRA;
import org.acra.annotation.AcraCore;
import org.acra.annotation.AcraDialog;
import org.acra.annotation.AcraMailSender;
import org.acra.data.StringFormat;
import timber.log.Timber;
@AcraCore(
buildConfigClass = BuildConfig.class,
resReportSendSuccessToast = R.string.crash_dialog_ok_toast,
reportFormat = StringFormat.KEY_VALUE_LIST,
reportContent = {USER_COMMENT, APP_VERSION_CODE, APP_VERSION_NAME, ANDROID_VERSION, PHONE_MODEL,
STACK_TRACE}
)
@AcraMailSender(
mailTo = "commons-app-android-private@googlegroups.com",
reportAsFile = false
)
@AcraDialog(
resTheme = R.style.Theme_AppCompat_Dialog,
resText = R.string.crash_dialog_text,
resTitle = R.string.crash_dialog_title,
resCommentPrompt = R.string.crash_dialog_comment_prompt
)
public class CommonsApplication extends MultiDexApplication {
public static final String loginMessageIntentKey = "loginMessage";
public static final String loginUsernameIntentKey = "loginUsername";
public static final String IS_LIMITED_CONNECTION_MODE_ENABLED = "is_limited_connection_mode_enabled";
@Inject
SessionManager sessionManager;
@Inject
DBOpenHelper dbOpenHelper;
@Inject
@Named("default_preferences")
JsonKvStore defaultPrefs;
@Inject
CommonsCookieJar cookieJar;
@Inject
CustomOkHttpNetworkFetcher customOkHttpNetworkFetcher;
/**
* Constants begin
*/
public static final int OPEN_APPLICATION_DETAIL_SETTINGS = 1001;
public static final String DEFAULT_EDIT_SUMMARY = "Uploaded using [[COM:MOA|Commons Mobile App]]";
public static final String FEEDBACK_EMAIL = "commons-app-android@googlegroups.com";
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App Feedback";
public static final String REPORT_EMAIL = "commons-app-android-private@googlegroups.com";
public static final String REPORT_EMAIL_SUBJECT = "Report a violation";
public static final String NOTIFICATION_CHANNEL_ID_ALL = "CommonsNotificationAll";
public static final String FEEDBACK_EMAIL_TEMPLATE_HEADER = "-- Technical information --";
/**
* Constants End
*/
private static CommonsApplication INSTANCE;
public static CommonsApplication getInstance() {
return INSTANCE;
}
private AppLanguageLookUpTable languageLookUpTable;
public AppLanguageLookUpTable getLanguageLookUpTable() {
return languageLookUpTable;
}
@Inject
ContributionDao contributionDao;
public static Boolean isPaused = false;
/**
* Used to declare and initialize various components and dependencies
*/
@Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
ACRA.init(this);
ApplicationlessInjection
.getInstance(this)
.getCommonsApplicationComponent()
.inject(this);
initTimber();
if (!defaultPrefs.getBoolean("has_user_manually_removed_location")) {
Set<String> defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS);
if (null == defaultExifTagsSet) {
defaultExifTagsSet = new HashSet<>();
}
defaultExifTagsSet.add(getString(R.string.exif_tag_location));
defaultPrefs.putStringSet(Prefs.MANAGED_EXIF_TAGS, defaultExifTagsSet);
}
// Set DownsampleEnabled to True to downsample the image in case it's heavy
ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(customOkHttpNetworkFetcher)
.setDownsampleEnabled(true)
.build();
try {
Fresco.initialize(this, config);
} catch (Exception e) {
Timber.e(e);
// TODO: Remove when we're able to initialize Fresco in test builds.
}
createNotificationChannel(this);
languageLookUpTable = new AppLanguageLookUpTable(this);
// This handler will catch exceptions thrown from Observables after they are disposed,
// or from Observables that are (deliberately or not) missing an onError handler.
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
// Fire progress callbacks for every 3% of uploaded content
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
}
/**
* Plants debug and file logging tree. Timber lets you plant your own logging trees.
*/
private void initTimber() {
boolean isBeta = ConfigUtils.isBetaFlavour();
String logFileName =
isBeta ? "CommonsBetaAppLogs" : "CommonsAppLogs";
String logDirectory = LogUtils.getLogDirectory();
//Delete stale logs if they have exceeded the specified size
deleteStaleLogs(logFileName, logDirectory);
FileLoggingTree tree = new FileLoggingTree(
Log.VERBOSE,
logFileName,
logDirectory,
1000,
getFileLoggingThreadPool());
Timber.plant(tree);
Timber.plant(new Timber.DebugTree());
}
/**
* Deletes the logs zip file at the specified directory and file locations specified in the
* params
*
* @param logFileName
* @param logDirectory
*/
private void deleteStaleLogs(String logFileName, String logDirectory) {
try {
File file = new File(logDirectory + "/zip/" + logFileName + ".zip");
if (file.exists() && file.getTotalSpace() > 1000000) {// In Kbs
file.delete();
}
} catch (Exception e) {
Timber.e(e);
}
}
public static boolean isRoboUnitTest() {
return "robolectric".equals(Build.FINGERPRINT);
}
private ThreadPoolService getFileLoggingThreadPool() {
return new ThreadPoolService.Builder("file-logging-thread")
.setPriority(Process.THREAD_PRIORITY_LOWEST)
.setPoolSize(1)
.setExceptionHandler(new BackgroundPoolExceptionHandler())
.build();
}
public static void createNotificationChannel(@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel = manager
.getNotificationChannel(NOTIFICATION_CHANNEL_ID_ALL);
if (channel == null) {
channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID_ALL,
context.getString(R.string.notifications_channel_name_all),
NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(channel);
}
}
}
public String getUserAgent() {
return "Commons/" + ConfigUtils.getVersionNameWithSha(this)
+ " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE;
}
/**
* clears data of current application
*
* @param context Application context
* @param logoutListener Implementation of interface LogoutListener
*/
@SuppressLint("CheckResult")
public void clearApplicationData(Context context, LogoutListener logoutListener) {
File cacheDirectory = context.getCacheDir();
File applicationDirectory = new File(cacheDirectory.getParent());
if (applicationDirectory.exists()) {
String[] fileNames = applicationDirectory.list();
for (String fileName : fileNames) {
if (!fileName.equals("lib")) {
FileUtils.deleteFile(new File(applicationDirectory, fileName));
}
}
}
sessionManager.logout()
.andThen(Completable.fromAction(() -> cookieJar.clear()))
.andThen(Completable.fromAction(() -> {
Timber.d("All accounts have been removed");
clearImageCache();
//TODO: fix preference manager
defaultPrefs.clearAll();
defaultPrefs.putBoolean("firstrun", false);
updateAllDatabases();
}
))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(logoutListener::onLogoutComplete, Timber::e);
}
/**
* Clear all images cache held by Fresco
*/
private void clearImageCache() {
ImagePipeline imagePipeline = Fresco.getImagePipeline();
imagePipeline.clearCaches();
}
/**
* Deletes all tables and re-creates them.
*/
private void updateAllDatabases() {
dbOpenHelper.getReadableDatabase().close();
SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
CategoryDao.Table.onDelete(db);
dbOpenHelper.deleteTable(db,
CONTRIBUTIONS_TABLE);//Delete the contributions table in the existing db on older versions
try {
contributionDao.deleteAll();
} catch (SQLiteException e) {
Timber.e(e);
}
BookmarkPicturesDao.Table.onDelete(db);
BookmarkLocationsDao.Table.onDelete(db);
Table.onDelete(db);
}
/**
* Interface used to get log-out events
*/
public interface LogoutListener {
void onLogoutComplete();
}
/**
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
* with relevant intent parameters. It does not perform the actual logout operation.
*/
public static class BaseLogoutListener implements CommonsApplication.LogoutListener {
Context ctx;
String loginMessage, userName;
/**
* Constructor for BaseLogoutListener.
*
* @param ctx Application context
*/
public BaseLogoutListener(final Context ctx) {
this.ctx = ctx;
}
/**
* Constructor for BaseLogoutListener
*
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
* @param loginMessage Message to be displayed on the login page
* @param loginUsername Username to be pre-filled on the login page
*/
public BaseLogoutListener(final Context ctx, final String loginMessage,
final String loginUsername) {
this.ctx = ctx;
this.loginMessage = loginMessage;
this.userName = loginUsername;
}
@Override
public void onLogoutComplete() {
Timber.d("Logout complete callback received.");
final Intent loginIntent = new Intent(ctx, LoginActivity.class);
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (loginMessage != null) {
loginIntent.putExtra(loginMessageIntentKey, loginMessage);
}
if (userName != null) {
loginIntent.putExtra(loginUsernameIntentKey, userName);
}
ctx.startActivity(loginIntent);
}
}
/**
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
*/
public static class ActivityLogoutListener extends BaseLogoutListener {
Activity activity;
/**
* Constructor for ActivityLogoutListener.
*
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
*/
public ActivityLogoutListener(final Activity activity, final Context ctx) {
super(ctx);
this.activity = activity;
}
/**
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
*
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
* @param loginMessage Message to be displayed on the login page after logout.
* @param loginUsername Username to be pre-filled on the login page after logout.
*/
public ActivityLogoutListener(final Activity activity, final Context ctx,
final String loginMessage, final String loginUsername) {
super(activity, loginMessage, loginUsername);
this.activity = activity;
}
@Override
public void onLogoutComplete() {
super.onLogoutComplete();
activity.finish();
}
}
}

View file

@ -0,0 +1,414 @@
package fr.free.nrw.commons
import android.annotation.SuppressLint
import android.app.Activity
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteException
import android.os.Build
import android.os.Process
import android.util.Log
import androidx.multidex.MultiDexApplication
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.imagepipeline.core.ImagePipelineConfig
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao
import fr.free.nrw.commons.category.CategoryDao
import fr.free.nrw.commons.concurrency.BackgroundPoolExceptionHandler
import fr.free.nrw.commons.concurrency.ThreadPoolService
import fr.free.nrw.commons.contributions.ContributionDao
import fr.free.nrw.commons.data.DBOpenHelper
import fr.free.nrw.commons.di.ApplicationlessInjection
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.language.AppLanguageLookUpTable
import fr.free.nrw.commons.logging.FileLoggingTree
import fr.free.nrw.commons.logging.LogUtils
import fr.free.nrw.commons.media.CustomOkHttpNetworkFetcher
import fr.free.nrw.commons.settings.Prefs
import fr.free.nrw.commons.upload.FileUtils
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
import io.reactivex.Completable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.internal.functions.Functions
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.schedulers.Schedulers
import org.acra.ACRA.init
import org.acra.ReportField
import org.acra.annotation.AcraCore
import org.acra.annotation.AcraDialog
import org.acra.annotation.AcraMailSender
import org.acra.data.StringFormat
import timber.log.Timber
import timber.log.Timber.DebugTree
import java.io.File
import javax.inject.Inject
import javax.inject.Named
@AcraCore(
buildConfigClass = BuildConfig::class,
resReportSendSuccessToast = R.string.crash_dialog_ok_toast,
reportFormat = StringFormat.KEY_VALUE_LIST,
reportContent = [ReportField.USER_COMMENT, ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.ANDROID_VERSION, ReportField.PHONE_MODEL, ReportField.STACK_TRACE]
)
@AcraMailSender(mailTo = "commons-app-android-private@googlegroups.com", reportAsFile = false)
@AcraDialog(
resTheme = R.style.Theme_AppCompat_Dialog,
resText = R.string.crash_dialog_text,
resTitle = R.string.crash_dialog_title,
resCommentPrompt = R.string.crash_dialog_comment_prompt
)
class CommonsApplication : MultiDexApplication() {
@Inject
lateinit var sessionManager: SessionManager
@Inject
lateinit var dbOpenHelper: DBOpenHelper
@Inject
@field:Named("default_preferences")
lateinit var defaultPrefs: JsonKvStore
@Inject
lateinit var cookieJar: CommonsCookieJar
@Inject
lateinit var customOkHttpNetworkFetcher: CustomOkHttpNetworkFetcher
var languageLookUpTable: AppLanguageLookUpTable? = null
private set
@Inject
lateinit var contributionDao: ContributionDao
/**
* Used to declare and initialize various components and dependencies
*/
override fun onCreate() {
super.onCreate()
instance = this
init(this)
ApplicationlessInjection
.getInstance(this)
.commonsApplicationComponent
.inject(this)
initTimber()
if (!defaultPrefs.getBoolean("has_user_manually_removed_location")) {
var defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS)
if (null == defaultExifTagsSet) {
defaultExifTagsSet = HashSet()
}
defaultExifTagsSet.add(getString(R.string.exif_tag_location))
defaultPrefs.putStringSet(Prefs.MANAGED_EXIF_TAGS, defaultExifTagsSet)
}
// Set DownsampleEnabled to True to downsample the image in case it's heavy
val config = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(customOkHttpNetworkFetcher)
.setDownsampleEnabled(true)
.build()
try {
Fresco.initialize(this, config)
} catch (e: Exception) {
Timber.e(e)
// TODO: Remove when we're able to initialize Fresco in test builds.
}
createNotificationChannel(this)
languageLookUpTable = AppLanguageLookUpTable(this)
// This handler will catch exceptions thrown from Observables after they are disposed,
// or from Observables that are (deliberately or not) missing an onError handler.
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer())
// Fire progress callbacks for every 3% of uploaded content
System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0")
}
/**
* Plants debug and file logging tree. Timber lets you plant your own logging trees.
*/
private fun initTimber() {
val isBeta = isBetaFlavour
val logFileName =
if (isBeta) "CommonsBetaAppLogs" else "CommonsAppLogs"
val logDirectory = LogUtils.getLogDirectory()
//Delete stale logs if they have exceeded the specified size
deleteStaleLogs(logFileName, logDirectory)
val tree = FileLoggingTree(
Log.VERBOSE,
logFileName,
logDirectory,
1000,
fileLoggingThreadPool
)
Timber.plant(tree)
Timber.plant(DebugTree())
}
/**
* Deletes the logs zip file at the specified directory and file locations specified in the
* params
*
* @param logFileName
* @param logDirectory
*/
private fun deleteStaleLogs(logFileName: String, logDirectory: String) {
try {
val file = File("$logDirectory/zip/$logFileName.zip")
if (file.exists() && file.totalSpace > 1000000) { // In Kbs
file.delete()
}
} catch (e: Exception) {
Timber.e(e)
}
}
private val fileLoggingThreadPool: ThreadPoolService
get() = ThreadPoolService.Builder("file-logging-thread")
.setPriority(Process.THREAD_PRIORITY_LOWEST)
.setPoolSize(1)
.setExceptionHandler(BackgroundPoolExceptionHandler())
.build()
val userAgent: String
get() = ("Commons/" + this.getVersionNameWithSha()
+ " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE)
/**
* clears data of current application
*
* @param context Application context
* @param logoutListener Implementation of interface LogoutListener
*/
@SuppressLint("CheckResult")
fun clearApplicationData(context: Context, logoutListener: LogoutListener) {
val cacheDirectory = context.cacheDir
val applicationDirectory = File(cacheDirectory.parent)
if (applicationDirectory.exists()) {
val fileNames = applicationDirectory.list()
for (fileName in fileNames) {
if (fileName != "lib") {
FileUtils.deleteFile(File(applicationDirectory, fileName))
}
}
}
sessionManager.logout()
.andThen(Completable.fromAction { cookieJar.clear() })
.andThen(Completable.fromAction {
Timber.d("All accounts have been removed")
clearImageCache()
//TODO: fix preference manager
defaultPrefs.clearAll()
defaultPrefs.putBoolean("firstrun", false)
updateAllDatabases()
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ logoutListener.onLogoutComplete() }, { t: Throwable? -> Timber.e(t) })
}
/**
* Clear all images cache held by Fresco
*/
private fun clearImageCache() {
val imagePipeline = Fresco.getImagePipeline()
imagePipeline.clearCaches()
}
/**
* Deletes all tables and re-creates them.
*/
private fun updateAllDatabases() {
dbOpenHelper.readableDatabase.close()
val db = dbOpenHelper.writableDatabase
CategoryDao.Table.onDelete(db)
dbOpenHelper.deleteTable(
db,
DBOpenHelper.CONTRIBUTIONS_TABLE
) //Delete the contributions table in the existing db on older versions
try {
contributionDao.deleteAll()
} catch (e: SQLiteException) {
Timber.e(e)
}
BookmarkPicturesDao.Table.onDelete(db)
BookmarkLocationsDao.Table.onDelete(db)
BookmarkItemsDao.Table.onDelete(db)
}
/**
* Interface used to get log-out events
*/
interface LogoutListener {
fun onLogoutComplete()
}
/**
* This listener is responsible for handling post-logout actions, specifically invoking the LoginActivity
* with relevant intent parameters. It does not perform the actual logout operation.
*/
open class BaseLogoutListener : LogoutListener {
var ctx: Context
var loginMessage: String? = null
var userName: String? = null
/**
* Constructor for BaseLogoutListener.
*
* @param ctx Application context
*/
constructor(ctx: Context) {
this.ctx = ctx
}
/**
* Constructor for BaseLogoutListener
*
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
* @param loginMessage Message to be displayed on the login page
* @param loginUsername Username to be pre-filled on the login page
*/
constructor(
ctx: Context, loginMessage: String?,
loginUsername: String?
) {
this.ctx = ctx
this.loginMessage = loginMessage
this.userName = loginUsername
}
override fun onLogoutComplete() {
Timber.d("Logout complete callback received.")
val loginIntent = Intent(ctx, LoginActivity::class.java)
loginIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (loginMessage != null) {
loginIntent.putExtra(LOGIN_MESSAGE_INTENT_KEY, loginMessage)
}
if (userName != null) {
loginIntent.putExtra(LOGIN_USERNAME_INTENT_KEY, userName)
}
ctx.startActivity(loginIntent)
}
}
/**
* This class is an extension of BaseLogoutListener, providing additional functionality or customization
* for the logout process. It includes specific actions to be taken during logout, such as handling redirection to the login screen.
*/
class ActivityLogoutListener : BaseLogoutListener {
var activity: Activity
/**
* Constructor for ActivityLogoutListener.
*
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
*/
constructor(activity: Activity, ctx: Context) : super(ctx) {
this.activity = activity
}
/**
* Constructor for ActivityLogoutListener with additional parameters for the login screen.
*
* @param activity The activity context from which the logout is initiated. Used to perform actions such as finishing the activity.
* @param ctx The application context, used for invoking the LoginActivity and passing relevant intent parameters as part of the post-logout process.
* @param loginMessage Message to be displayed on the login page after logout.
* @param loginUsername Username to be pre-filled on the login page after logout.
*/
constructor(
activity: Activity, ctx: Context?,
loginMessage: String?, loginUsername: String?
) : super(activity, loginMessage, loginUsername) {
this.activity = activity
}
override fun onLogoutComplete() {
super.onLogoutComplete()
activity.finish()
}
}
companion object {
const val LOGIN_MESSAGE_INTENT_KEY: String = "loginMessage"
const val LOGIN_USERNAME_INTENT_KEY: String = "loginUsername"
const val IS_LIMITED_CONNECTION_MODE_ENABLED: String = "is_limited_connection_mode_enabled"
/**
* Constants begin
*/
const val OPEN_APPLICATION_DETAIL_SETTINGS: Int = 1001
const val DEFAULT_EDIT_SUMMARY: String = "Uploaded using [[COM:MOA|Commons Mobile App]]"
const val FEEDBACK_EMAIL: String = "commons-app-android@googlegroups.com"
const val FEEDBACK_EMAIL_SUBJECT: String = "Commons Android App Feedback"
const val REPORT_EMAIL: String = "commons-app-android-private@googlegroups.com"
const val REPORT_EMAIL_SUBJECT: String = "Report a violation"
const val NOTIFICATION_CHANNEL_ID_ALL: String = "CommonsNotificationAll"
const val FEEDBACK_EMAIL_TEMPLATE_HEADER: String = "-- Technical information --"
/**
* Constants End
*/
@JvmStatic
lateinit var instance: CommonsApplication
private set
@JvmField
var isPaused: Boolean = false
@JvmStatic
fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val manager = context
.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
var channel = manager
.getNotificationChannel(NOTIFICATION_CHANNEL_ID_ALL)
if (channel == null) {
channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID_ALL,
context.getString(R.string.notifications_channel_name_all),
NotificationManager.IMPORTANCE_DEFAULT
)
manager.createNotificationChannel(channel)
}
}
}
}
}

View file

@ -53,6 +53,7 @@ import fr.free.nrw.commons.utils.SystemThemeUtils;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Named;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
@ -301,7 +302,8 @@ public class LocationPickerActivity extends BaseActivity implements
modifyLocationButton = findViewById(R.id.modify_location);
removeLocationButton = findViewById(R.id.remove_location);
showInMapButton = findViewById(R.id.show_in_map);
showInMapButton.setText(getResources().getString(R.string.show_in_map_app).toUpperCase());
showInMapButton.setText(getResources().getString(R.string.show_in_map_app).toUpperCase(
Locale.ROOT));
shadow = findViewById(R.id.location_picker_image_view_shadow);
}

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons
import android.os.Parcelable
import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.wikidata.model.page.PageTitle
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.util.Date
import java.util.Locale
@ -124,6 +125,7 @@ class Media constructor(
* Gets the categories the file falls under.
* @return file categories as an ArrayList of Strings
*/
@IgnoredOnParcel
var addedCategories: List<String>? = null
// TODO added categories should be removed. It is added for a short fix. On category update,
// categories should be re-fetched instead

View file

@ -32,7 +32,7 @@ class ThanksClient
revisionId.toString(), // Rev
null, // Log
csrfTokenClient.getTokenBlocking(), // Token
CommonsApplication.getInstance().userAgent, // Source
CommonsApplication.instance.userAgent, // Source
).map { mwThankPostResponse ->
mwThankPostResponse.result?.success == 1
}

View file

@ -50,8 +50,8 @@ import timber.log.Timber;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
import static fr.free.nrw.commons.CommonsApplication.loginMessageIntentKey;
import static fr.free.nrw.commons.CommonsApplication.loginUsernameIntentKey;
import static fr.free.nrw.commons.CommonsApplication.LOGIN_MESSAGE_INTENT_KEY;
import static fr.free.nrw.commons.CommonsApplication.LOGIN_USERNAME_INTENT_KEY;
public class LoginActivity extends AccountAuthenticatorActivity {
@ -94,8 +94,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
binding = ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
String message = getIntent().getStringExtra(loginMessageIntentKey);
String username = getIntent().getStringExtra(loginUsernameIntentKey);
String message = getIntent().getStringExtra(LOGIN_MESSAGE_INTENT_KEY);
String username = getIntent().getStringExtra(LOGIN_USERNAME_INTENT_KEY);
binding.loginUsername.addTextChangedListener(textWatcher);
binding.loginPassword.addTextChangedListener(textWatcher);

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.bookmarks.items;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -134,6 +135,7 @@ public class BookmarkItemsDao {
* @param cursor : Object for storing database data
* @return DepictedItem
*/
@SuppressLint("Range")
DepictedItem fromCursor(final Cursor cursor) {
final String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME));
final String description

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.bookmarks.locations;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -146,6 +147,7 @@ public class BookmarkLocationsDao {
return false;
}
@SuppressLint("Range")
@NonNull
Place fromCursor(final Cursor cursor) {
final LatLng location = new LatLng(cursor.getDouble(cursor.getColumnIndex(Table.COLUMN_LAT)),

View file

@ -9,6 +9,7 @@ import android.view.ViewGroup;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
@ -33,6 +34,23 @@ public class BookmarkLocationsFragment extends DaggerFragment {
@Inject BookmarkLocationsDao bookmarkLocationDao;
@Inject CommonPlaceClickActions commonPlaceClickActions;
private PlaceAdapter adapter;
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
contributionController.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
});
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
@ -45,7 +63,7 @@ public class BookmarkLocationsFragment extends DaggerFragment {
contributionController.locationPermissionCallback.onLocationPermissionGranted();
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher);
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
} else {
contributionController.locationPermissionCallback.onLocationPermissionDenied(getActivity().getString(R.string.in_app_camera_location_permission_denied));
}
@ -83,7 +101,9 @@ public class BookmarkLocationsFragment extends DaggerFragment {
return Unit.INSTANCE;
},
commonPlaceClickActions,
inAppCameraLocationPermissionLauncher
inAppCameraLocationPermissionLauncher,
galleryPickLauncherForResult,
cameraPickLauncherForResult
);
binding.listView.setAdapter(adapter);
}
@ -109,11 +129,6 @@ public class BookmarkLocationsFragment extends DaggerFragment {
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
contributionController.handleActivityResult(getActivity(), requestCode, resultCode, data);
}
@Override
public void onDestroy() {
super.onDestroy();

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.bookmarks.pictures;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -150,6 +151,7 @@ public class BookmarkPicturesDao {
return false;
}
@SuppressLint("Range")
@NonNull
Bookmark fromCursor(Cursor cursor) {
String fileName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_MEDIA_NAME));

View file

@ -124,7 +124,7 @@ class CategoryClient
}.map {
it
.filter { page ->
page.categoryInfo() == null || !page.categoryInfo().isHidden
!page.categoryInfo().isHidden
}.map {
CategoryItem(
it.title().replace(CATEGORY_PREFIX, ""),

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.category;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -111,6 +112,7 @@ public class CategoryDao {
}
@NonNull
@SuppressLint("Range")
Category fromCursor(Cursor cursor) {
// Hardcoding column positions!
return new Category(

View file

@ -7,6 +7,7 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
@ -64,10 +65,11 @@ public class ContributionController {
* Check for permissions and initiate camera click
*/
public void initiateCameraPick(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
ActivityResultLauncher<Intent> resultLauncher) {
boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true);
if (!useExtStorage) {
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
return;
}
@ -75,12 +77,12 @@ public class ContributionController {
() -> {
if (defaultKvStore.getBoolean("inAppCameraFirstRun")) {
defaultKvStore.putBoolean("inAppCameraFirstRun", false);
askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher);
askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher, resultLauncher);
} else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) {
createDialogsAndHandleLocationPermissions(activity,
inAppCameraLocationPermissionLauncher);
inAppCameraLocationPermissionLauncher, resultLauncher);
} else {
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
}
},
R.string.storage_permission_title,
@ -94,7 +96,8 @@ public class ContributionController {
* @param activity
*/
private void createDialogsAndHandleLocationPermissions(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
ActivityResultLauncher<Intent> resultLauncher) {
locationPermissionCallback = new LocationPermissionCallback() {
@Override
public void onLocationPermissionDenied(String toastMessage) {
@ -103,16 +106,16 @@ public class ContributionController {
toastMessage,
Toast.LENGTH_LONG
).show();
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
}
@Override
public void onLocationPermissionGranted() {
if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
showLocationOffDialog(activity, R.string.in_app_camera_needs_location,
R.string.in_app_camera_location_unavailable);
R.string.in_app_camera_location_unavailable, resultLauncher);
} else {
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
}
}
};
@ -135,9 +138,10 @@ public class ContributionController {
* @param activity Activity reference
* @param dialogTextResource Resource id of text to be shown in dialog
* @param toastTextResource Resource id of text to be shown in toast
* @param resultLauncher
*/
private void showLocationOffDialog(Activity activity, int dialogTextResource,
int toastTextResource) {
int toastTextResource, ActivityResultLauncher<Intent> resultLauncher) {
DialogUtil
.showAlertDialog(activity,
activity.getString(R.string.ask_to_turn_location_on),
@ -148,20 +152,21 @@ public class ContributionController {
() -> {
Toast.makeText(activity, activity.getString(toastTextResource),
Toast.LENGTH_LONG).show();
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
}
);
}
public void handleShowRationaleFlowCameraLocation(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
ActivityResultLauncher<Intent> resultLauncher) {
DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title),
activity.getString(R.string.in_app_camera_location_permission_rationale),
activity.getString(android.R.string.ok),
activity.getString(android.R.string.cancel),
() -> {
createDialogsAndHandleLocationPermissions(activity,
inAppCameraLocationPermissionLauncher);
inAppCameraLocationPermissionLauncher, resultLauncher);
},
() -> locationPermissionCallback.onLocationPermissionDenied(
activity.getString(R.string.in_app_camera_location_permission_denied)),
@ -181,7 +186,8 @@ public class ContributionController {
* @param activity
*/
private void askUserToAllowLocationAccess(Activity activity,
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher) {
ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher,
ActivityResultLauncher<Intent> resultLauncher) {
DialogUtil.showAlertDialog(activity,
activity.getString(R.string.in_app_camera_location_permission_title),
activity.getString(R.string.in_app_camera_location_access_explanation),
@ -190,12 +196,12 @@ public class ContributionController {
() -> {
defaultKvStore.putBoolean("inAppCameraLocationPref", true);
createDialogsAndHandleLocationPermissions(activity,
inAppCameraLocationPermissionLauncher);
inAppCameraLocationPermissionLauncher, resultLauncher);
},
() -> {
ViewUtil.showLongToast(activity, R.string.in_app_camera_location_permission_denied);
defaultKvStore.putBoolean("inAppCameraLocationPref", false);
initiateCameraUpload(activity);
initiateCameraUpload(activity, resultLauncher);
},
null,
true);
@ -204,18 +210,18 @@ public class ContributionController {
/**
* Initiate gallery picker
*/
public void initiateGalleryPick(final Activity activity, final boolean allowMultipleUploads) {
initiateGalleryUpload(activity, allowMultipleUploads);
public void initiateGalleryPick(final Activity activity, ActivityResultLauncher<Intent> resultLauncher, final boolean allowMultipleUploads) {
initiateGalleryUpload(activity, resultLauncher, allowMultipleUploads);
}
/**
* Initiate gallery picker with permission
*/
public void initiateCustomGalleryPickWithPermission(final Activity activity) {
public void initiateCustomGalleryPickWithPermission(final Activity activity, ActivityResultLauncher<Intent> resultLauncher) {
setPickerConfiguration(activity, true);
PermissionUtils.checkPermissionsAndPerformAction(activity,
() -> FilePicker.openCustomSelector(activity, 0),
() -> FilePicker.openCustomSelector(activity, resultLauncher, 0),
R.string.storage_permission_title,
R.string.write_storage_permission_rationale,
PermissionUtils.PERMISSIONS_STORAGE);
@ -225,12 +231,10 @@ public class ContributionController {
/**
* Open chooser for gallery uploads
*/
private void initiateGalleryUpload(final Activity activity,
private void initiateGalleryUpload(final Activity activity, ActivityResultLauncher<Intent> resultLauncher,
final boolean allowMultipleUploads) {
setPickerConfiguration(activity, allowMultipleUploads);
boolean openDocumentIntentPreferred = defaultKvStore.getBoolean(
"openDocumentPhotoPickerPref", true);
FilePicker.openGallery(activity, 0, openDocumentIntentPreferred);
FilePicker.openGallery(activity, resultLauncher, 0, isDocumentPhotoPickerPreferred());
}
/**
@ -247,22 +251,43 @@ public class ContributionController {
/**
* Initiate camera upload by opening camera
*/
private void initiateCameraUpload(Activity activity) {
private void initiateCameraUpload(Activity activity, ActivityResultLauncher<Intent> resultLauncher) {
setPickerConfiguration(activity, false);
if (defaultKvStore.getBoolean("inAppCameraLocationPref", false)) {
locationBeforeImageCapture = locationManager.getLastLocation();
}
isInAppCameraUpload = true;
FilePicker.openCameraForImage(activity, 0);
FilePicker.openCameraForImage(activity, resultLauncher, 0);
}
private boolean isDocumentPhotoPickerPreferred(){
return defaultKvStore.getBoolean(
"openDocumentPhotoPickerPref", true);
}
public void onPictureReturnedFromGallery(ActivityResult result, Activity activity, FilePicker.Callbacks callbacks){
if(isDocumentPhotoPickerPreferred()){
FilePicker.onPictureReturnedFromDocuments(result, activity, callbacks);
} else {
FilePicker.onPictureReturnedFromGallery(result, activity, callbacks);
}
}
public void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
FilePicker.onPictureReturnedFromCustomSelector(result, activity, callbacks);
}
public void onPictureReturnedFromCamera(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
FilePicker.onPictureReturnedFromCamera(result, activity, callbacks);
}
/**
* Attaches callback for file picker.
*/
public void handleActivityResult(Activity activity, int requestCode, int resultCode,
Intent data) {
FilePicker.handleActivityResult(requestCode, resultCode, data, activity,
new DefaultCallback() {
public void handleActivityResultWithCallback(Activity activity, FilePicker.HandleActivityResult handleActivityResult) {
handleActivityResult.onHandleActivityResult(new DefaultCallback() {
@Override
public void onCanceled(final ImageSource source, final int type) {

View file

@ -6,6 +6,7 @@ import static fr.free.nrw.commons.di.NetworkingModule.NAMED_LANGUAGE_WIKI_PEDIA_
import android.Manifest.permission;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
@ -20,6 +21,7 @@ import android.widget.LinearLayout;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@ -96,6 +98,30 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private int contributionsSize;
private String userName;
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
new RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@ -111,7 +137,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller.handleShowRationaleFlowCameraLocation(getActivity(),
inAppCameraLocationPermissionLauncher);
inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
} else {
controller.locationPermissionCallback.onLocationPermissionDenied(
getActivity().getString(
@ -322,7 +348,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
private void setListeners() {
binding.fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
binding.fabCamera.setOnClickListener(view -> {
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
animateFAB(isFabOpen);
});
binding.fabCamera.setOnLongClickListener(view -> {
@ -330,7 +356,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
return true;
});
binding.fabGallery.setOnClickListener(view -> {
controller.initiateGalleryPick(getActivity(), true);
controller.initiateGalleryPick(getActivity(), galleryPickLauncherForResult, true);
animateFAB(isFabOpen);
});
binding.fabGallery.setOnLongClickListener(view -> {
@ -343,7 +369,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
* Launch Custom Selector.
*/
protected void launchCustomSelector() {
controller.initiateCustomGalleryPickWithPermission(getActivity());
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult);
animateFAB(isFabOpen);
}

View file

@ -438,13 +438,6 @@ public class MainActivity extends BaseActivity
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Timber.d(data != null ? data.toString() : "onActivityResult data is null");
super.onActivityResult(requestCode, resultCode, data);
controller.handleActivityResult(this, requestCode, resultCode, data);
}
@Override
protected void onResume() {
super.onResume();

View file

@ -22,7 +22,7 @@ class WikipediaInstructionsDialogFragment : DialogFragment() {
) = DialogAddToWikipediaInstructionsBinding
.inflate(inflater, container, false)
.apply {
val contribution: Contribution? = arguments!!.getParcelable(ARG_CONTRIBUTION)
val contribution: Contribution? = requireArguments().getParcelable(ARG_CONTRIBUTION)
tvWikicode.setText(contribution?.media?.wikiCode)
instructionsCancel.setOnClickListener { dismiss() }
instructionsConfirm.setOnClickListener {

View file

@ -13,6 +13,8 @@ import android.view.Window
import android.widget.Button
import android.widget.ImageButton
import android.widget.TextView
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -41,7 +43,6 @@ import fr.free.nrw.commons.R
import fr.free.nrw.commons.customselector.database.NotForUploadStatus
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants
import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.SHOULD_REFRESH
import fr.free.nrw.commons.customselector.listeners.FolderClickListener
import fr.free.nrw.commons.customselector.listeners.ImageSelectListener
import fr.free.nrw.commons.customselector.model.Image
@ -147,6 +148,10 @@ class CustomSelectorActivity :
private var showPartialAccessIndicator by mutableStateOf(false)
private val startForResult = registerForActivityResult(StartActivityForResult()){ result ->
onFullScreenDataReceived(result)
}
/**
* onCreate Activity, sets theme, initialises the view model, setup view.
*/
@ -225,20 +230,12 @@ class CustomSelectorActivity :
/**
* When data will be send from full screen mode, it will be passed to fragment
*/
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?,
) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE &&
resultCode == Activity.RESULT_OK
) {
private fun onFullScreenDataReceived(result: ActivityResult){
if (result.resultCode == Activity.RESULT_OK) {
val selectedImages: ArrayList<Image> =
data!!
result.data!!
.getParcelableArrayListExtra(CustomSelectorConstants.NEW_SELECTED_IMAGES)!!
val shouldRefresh = data.getBooleanExtra(SHOULD_REFRESH, false)
imageFragment?.passSelectedImages(selectedImages, shouldRefresh)
viewModel?.selectedImages?.value = selectedImages
}
}
@ -511,7 +508,7 @@ class CustomSelectorActivity :
selectedImages,
)
intent.putExtra(CustomSelectorConstants.BUCKET_ID, bucketId)
startActivityForResult(intent, Constants.RequestCodes.RECEIVE_DATA_FROM_FULL_SCREEN_MODE)
startForResult.launch(intent)
}
/**

View file

@ -279,11 +279,17 @@ class ImageFragment :
filteredImages = ImageHelper.filterImages(images, bucketId)
allImages = ArrayList(filteredImages)
imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions)
viewModel?.selectedImages?.value?.let { selectedImages ->
imageAdapter.setSelectedImages(selectedImages)
}
imageAdapter.notifyDataSetChanged()
selectorRV?.let {
it.visibility = View.VISIBLE
lastItemId?.let { pos ->
(it.layoutManager as GridLayoutManager)
.scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos))
if (switch?.isChecked == false) {
lastItemId?.let { pos ->
(it.layoutManager as GridLayoutManager)
.scrollToPosition(ImageHelper.getIndexFromId(filteredImages, pos))
}
}
}
} else {
@ -382,14 +388,6 @@ class ImageFragment :
selectedImages: ArrayList<Image>,
shouldRefresh: Boolean,
) {
imageAdapter.setSelectedImages(selectedImages)
val uploadingContributions = getUploadingContributions()
if (!showAlreadyActionedImages && shouldRefresh) {
imageAdapter.init(filteredImages, allImages, TreeMap(), uploadingContributions)
imageAdapter.setSelectedImages(selectedImages)
}
}
/**

View file

@ -17,6 +17,7 @@ import fr.free.nrw.commons.utils.CustomSelectorUtils
import fr.free.nrw.commons.utils.CustomSelectorUtils.Companion.checkWhetherFileExistsOnCommonsUsingSHA1
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import java.util.Calendar

View file

@ -6,6 +6,8 @@ import android.os.Bundle
import android.os.Parcelable
import android.speech.RecognizerIntent
import android.view.View
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import fr.free.nrw.commons.CommonsApplication
@ -70,10 +72,14 @@ class DescriptionEditActivity :
private lateinit var binding: ActivityDescriptionEditBinding
private val requestCodeForVoiceInput = 1213
private var descriptionAndCaptions: ArrayList<UploadMediaDetail>? = null
private val voiceInputResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
onVoiceInput(result)
}
@Inject lateinit var descriptionEditHelper: DescriptionEditHelper
@Inject lateinit var sessionManager: SessionManager
@ -115,6 +121,7 @@ class DescriptionEditActivity :
savedLanguageValue,
descriptionAndCaptions,
recentLanguagesDao,
voiceInputResultLauncher
)
uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int ->
showInfoAlert(
@ -149,6 +156,15 @@ class DescriptionEditActivity :
override fun onPrimaryCaptionTextChange(isNotEmpty: Boolean) {}
private fun onVoiceInput(result: ActivityResult) {
if (result.resultCode == RESULT_OK && result.data != null) {
val resultData = result.data!!.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
uploadMediaDetailAdapter.handleSpeechResult(resultData!![0])
} else {
Timber.e("Error %s", result.resultCode)
}
}
/**
* Adds new language item to RecyclerView
*/
@ -221,7 +237,7 @@ class DescriptionEditActivity :
) {
try {
descriptionEditHelper
?.addDescription(
.addDescription(
applicationContext,
media,
updatedWikiText,
@ -234,7 +250,7 @@ class DescriptionEditActivity :
)
}
} catch (e: InvalidLoginTokenException) {
val username: String? = sessionManager?.userName
val username: String? = sessionManager.userName
val logoutListener =
CommonsApplication.BaseLogoutListener(
this,
@ -242,7 +258,7 @@ class DescriptionEditActivity :
username,
)
val commonsApplication = CommonsApplication.getInstance()
val commonsApplication = CommonsApplication.instance
if (commonsApplication != null) {
commonsApplication.clearApplicationData(this, logoutListener)
}
@ -252,7 +268,7 @@ class DescriptionEditActivity :
for (mediaDetail in uploadMediaDetails) {
try {
compositeDisposable.add(
descriptionEditHelper!!
descriptionEditHelper
.addCaption(
applicationContext,
media,
@ -275,7 +291,7 @@ class DescriptionEditActivity :
username,
)
val commonsApplication = CommonsApplication.getInstance()
val commonsApplication = CommonsApplication.instance
if (commonsApplication != null) {
commonsApplication.clearApplicationData(this, logoutListener)
}
@ -292,22 +308,6 @@ class DescriptionEditActivity :
progressDialog!!.show()
}
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?,
) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == requestCodeForVoiceInput) {
if (resultCode == RESULT_OK && data != null) {
val result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
uploadMediaDetailAdapter.handleSpeechResult(result!![0])
} else {
Timber.e("Error %s", resultCode)
}
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)

View file

@ -65,7 +65,6 @@ class TransformImageImpl : TransformImage {
} catch (e: LLJTranException) {
Timber.tag("Error").d(e)
return null
false
}
if (rotated) {

View file

@ -22,6 +22,7 @@ import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.utils.ActivityUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Named;
@ -112,13 +113,13 @@ public class ExploreFragment extends CommonsDaggerSupportFragment {
mobileRootFragment = new ExploreListRootFragment(mobileArguments);
mapRootFragment = new ExploreMapRootFragment(mapArguments);
fragmentList.add(featuredRootFragment);
titleList.add(getString(R.string.explore_tab_title_featured).toUpperCase());
titleList.add(getString(R.string.explore_tab_title_featured).toUpperCase(Locale.ROOT));
fragmentList.add(mobileRootFragment);
titleList.add(getString(R.string.explore_tab_title_mobile).toUpperCase());
titleList.add(getString(R.string.explore_tab_title_mobile).toUpperCase(Locale.ROOT));
fragmentList.add(mapRootFragment);
titleList.add(getString(R.string.explore_tab_title_map).toUpperCase());
titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT));
((MainActivity)getActivity()).showTabs();
((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);

View file

@ -28,6 +28,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import timber.log.Timber;
@ -95,11 +96,11 @@ public class SearchActivity extends BaseActivity
searchDepictionsFragment = new SearchDepictionsFragment();
searchCategoryFragment= new SearchCategoryFragment();
fragmentList.add(searchMediaFragment);
titleList.add(getResources().getString(R.string.search_tab_title_media).toUpperCase());
titleList.add(getResources().getString(R.string.search_tab_title_media).toUpperCase(Locale.ROOT));
fragmentList.add(searchCategoryFragment);
titleList.add(getResources().getString(R.string.search_tab_title_categories).toUpperCase());
titleList.add(getResources().getString(R.string.search_tab_title_categories).toUpperCase(Locale.ROOT));
fragmentList.add(searchDepictionsFragment);
titleList.add(getResources().getString(R.string.search_tab_title_depictions).toUpperCase());
titleList.add(getResources().getString(R.string.search_tab_title_depictions).toUpperCase(Locale.ROOT));
viewPagerAdapter.setTabData(fragmentList, titleList);
viewPagerAdapter.notifyDataSetChanged();

View file

@ -18,6 +18,6 @@ class CategoriesMediaFragment : PageableMediaFragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}")
onQueryUpdated("$CATEGORY_PREFIX${requireArguments().getString("categoryName")!!}")
}
}

View file

@ -21,6 +21,6 @@ class ParentCategoriesFragment : PageableCategoryFragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}")
onQueryUpdated("$CATEGORY_PREFIX${requireArguments().getString("categoryName")!!}")
}
}

View file

@ -20,6 +20,6 @@ class SubCategoriesFragment : PageableCategoryFragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated("$CATEGORY_PREFIX${arguments!!.getString("categoryName")!!}")
onQueryUpdated("$CATEGORY_PREFIX${requireArguments().getString("categoryName")!!}")
}
}

View file

@ -13,13 +13,13 @@ class ChildDepictionsFragment : PageableDepictionsFragment() {
override val injectedPresenter
get() = presenter
override fun getEmptyText(query: String) = getString(R.string.no_child_classes, arguments!!.getString("wikidataItemName")!!)
override fun getEmptyText(query: String) = getString(R.string.no_child_classes, requireArguments().getString("wikidataItemName")!!)
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated(arguments!!.getString("entityId")!!)
onQueryUpdated(requireArguments().getString("entityId")!!)
}
}

View file

@ -17,6 +17,6 @@ class DepictedImagesFragment : PageableMediaFragment() {
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated(arguments!!.getString("entityId")!!)
onQueryUpdated(requireArguments().getString("entityId")!!)
}
}

View file

@ -13,13 +13,13 @@ class ParentDepictionsFragment : PageableDepictionsFragment() {
override val injectedPresenter
get() = presenter
override fun getEmptyText(query: String) = getString(R.string.no_parent_classes, arguments!!.getString("wikidataItemName")!!)
override fun getEmptyText(query: String) = getString(R.string.no_parent_classes, requireArguments().getString("wikidataItemName")!!)
override fun onViewCreated(
view: View,
savedInstanceState: Bundle?,
) {
super.onViewCreated(view, savedInstanceState)
onQueryUpdated(arguments!!.getString("entityId")!!)
onQueryUpdated(requireArguments().getString("entityId")!!)
}
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.explore.recentsearches;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -178,6 +179,7 @@ public class RecentSearchesDao {
* @return RecentSearch object
*/
@NonNull
@SuppressLint("Range")
RecentSearch fromCursor(Cursor cursor) {
// Hardcoding column positions!
return new RecentSearch(

View file

@ -15,6 +15,7 @@ import fr.free.nrw.commons.databinding.FragmentSearchHistoryBinding;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.SearchActivity;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
@ -90,7 +91,7 @@ public class RecentSearchesFragment extends CommonsDaggerSupportFragment {
private void showDeleteAlertDialog(@NonNull final Context context, final int position) {
new AlertDialog.Builder(context)
.setMessage(R.string.delete_search_dialog)
.setPositiveButton(getString(R.string.delete).toUpperCase(),
.setPositiveButton(getString(R.string.delete).toUpperCase(Locale.ROOT),
((dialog, which) -> setDeletePositiveButton(context, dialog, position)))
.setNegativeButton(android.R.string.cancel, null)
.create()

View file

@ -4,20 +4,11 @@ public interface Constants {
String DEFAULT_FOLDER_NAME = "CommonsContributions";
/**
* Provides the request codes utilised by the FilePicker
* Provides the request codes for permission handling
*/
interface RequestCodes {
int LOCATION = 1;
int STORAGE = 2;
int FILE_PICKER_IMAGE_IDENTIFICATOR = 0b1101101100; //876
int SOURCE_CHOOSER = 1 << 15;
int PICK_PICTURE_FROM_CUSTOM_SELECTOR = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 10);
int PICK_PICTURE_FROM_DOCUMENTS = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 11);
int PICK_PICTURE_FROM_GALLERY = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 12);
int TAKE_PICTURE = FILE_PICKER_IMAGE_IDENTIFICATOR + (1 << 13);
int RECEIVE_DATA_FROM_FULL_SCREEN_MODE = 1 << 9;
}
/**

View file

@ -12,6 +12,8 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
@ -107,31 +109,25 @@ public class FilePicker implements Constants {
*
* @param type Custom type of your choice, which will be returned with the images
*/
public static void openGallery(Activity activity, int type, boolean openDocumentIntentPreferred) {
public static void openGallery(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type, boolean openDocumentIntentPreferred) {
Intent intent = createGalleryIntent(activity, type, openDocumentIntentPreferred);
int requestCode = RequestCodes.PICK_PICTURE_FROM_GALLERY;
if(openDocumentIntentPreferred){
requestCode = RequestCodes.PICK_PICTURE_FROM_DOCUMENTS;
}
activity.startActivityForResult(intent, requestCode);
resultLauncher.launch(intent);
}
/**
* Opens Custom Selector
*/
public static void openCustomSelector(Activity activity, int type) {
public static void openCustomSelector(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type) {
Intent intent = createCustomSelectorIntent(activity, type);
activity.startActivityForResult(intent, RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR);
resultLauncher.launch(intent);
}
/**
* Opens the camera app to pick image clicked by user
*/
public static void openCameraForImage(Activity activity, int type) {
public static void openCameraForImage(Activity activity, ActivityResultLauncher<Intent> resultLauncher, int type) {
Intent intent = createCameraForImageIntent(activity, type);
activity.startActivityForResult(intent, RequestCodes.TAKE_PICTURE);
resultLauncher.launch(intent);
}
@Nullable
@ -154,43 +150,6 @@ public class FilePicker implements Constants {
}
}
/**
* Any activity can use this method to attach their callback to the file picker
*/
public static void handleActivityResult(int requestCode, int resultCode, Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
boolean isHandledPickedFile = (requestCode & RequestCodes.FILE_PICKER_IMAGE_IDENTIFICATOR) > 0;
if (isHandledPickedFile) {
requestCode &= ~RequestCodes.SOURCE_CHOOSER;
if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY ||
requestCode == RequestCodes.TAKE_PICTURE ||
requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS ||
requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS && !isPhoto(data)) {
onPictureReturnedFromDocuments(data, activity, callbacks);
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY && !isPhoto(data)) {
onPictureReturnedFromGallery(data, activity, callbacks);
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR) {
onPictureReturnedFromCustomSelector(data, activity, callbacks);
} else if (requestCode == RequestCodes.TAKE_PICTURE) {
onPictureReturnedFromCamera(activity, callbacks);
}
} else {
if (requestCode == RequestCodes.PICK_PICTURE_FROM_DOCUMENTS) {
callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_GALLERY) {
callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity));
} else if (requestCode == RequestCodes.PICK_PICTURE_FROM_CUSTOM_SELECTOR){
callbacks.onCanceled(ImageSource.CUSTOM_SELECTOR, restoreType(activity));
}
else {
callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
}
}
}
}
public static List<UploadableFile> handleExternalImagesPicked(Intent data, Activity activity) {
try {
return getFilesFromGalleryPictures(data, activity);
@ -243,18 +202,22 @@ public class FilePicker implements Constants {
return intent;
}
private static void onPictureReturnedFromDocuments(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
Uri photoPath = data.getData();
UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath);
callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
public static void onPictureReturnedFromDocuments(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){
try {
Uri photoPath = result.getData().getData();
UploadableFile photoFile = PickedFiles.pickedExistingPicture(activity, photoPath);
callbacks.onImagesPicked(singleFileList(photoFile), FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
if (configuration(activity).shouldCopyPickedImagesToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
}
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
}
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
} else {
callbacks.onCanceled(FilePicker.ImageSource.DOCUMENTS, restoreType(activity));
}
}
@ -262,14 +225,18 @@ public class FilePicker implements Constants {
* onPictureReturnedFromCustomSelector.
* Retrieve and forward the images to upload wizard through callback.
*/
private static void onPictureReturnedFromCustomSelector(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
List<UploadableFile> files = getFilesFromCustomSelector(data, activity);
callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
}
public static void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
if(result.getResultCode() == Activity.RESULT_OK){
try {
List<UploadableFile> files = getFilesFromCustomSelector(result.getData(), activity);
callbacks.onImagesPicked(files, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, ImageSource.CUSTOM_SELECTOR, restoreType(activity));
}
} else {
callbacks.onCanceled(ImageSource.CUSTOM_SELECTOR, restoreType(activity));
}
}
/**
@ -292,13 +259,17 @@ public class FilePicker implements Constants {
return files;
}
private static void onPictureReturnedFromGallery(Intent data, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
List<UploadableFile> files = getFilesFromGalleryPictures(data, activity);
callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity));
public static void onPictureReturnedFromGallery(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
if(result.getResultCode() == Activity.RESULT_OK && !isPhoto(result.getData())){
try {
List<UploadableFile> files = getFilesFromGalleryPictures(result.getData(), activity);
callbacks.onImagesPicked(files, FilePicker.ImageSource.GALLERY, restoreType(activity));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.GALLERY, restoreType(activity));
}
} else{
callbacks.onCanceled(FilePicker.ImageSource.GALLERY, restoreType(activity));
}
}
@ -324,69 +295,40 @@ public class FilePicker implements Constants {
return files;
}
private static void onPictureReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null);
if (!TextUtils.isEmpty(lastImageUri)) {
revokeWritePermission(activity, Uri.parse(lastImageUri));
}
UploadableFile photoFile = FilePicker.takenCameraPicture(activity);
List<UploadableFile> files = new ArrayList<>();
files.add(photoFile);
if (photoFile == null) {
Exception e = new IllegalStateException("Unable to get the picture returned from camera");
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
} else {
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
public static void onPictureReturnedFromCamera(ActivityResult activityResult, Activity activity, @NonNull FilePicker.Callbacks callbacks) {
if(activityResult.getResultCode() == Activity.RESULT_OK){
try {
String lastImageUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_PHOTO_URI, null);
if (!TextUtils.isEmpty(lastImageUri)) {
revokeWritePermission(activity, Uri.parse(lastImageUri));
}
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
UploadableFile photoFile = FilePicker.takenCameraPicture(activity);
List<UploadableFile> files = new ArrayList<>();
files.add(photoFile);
PreferenceManager.getDefaultSharedPreferences(activity)
if (photoFile == null) {
Exception e = new IllegalStateException("Unable to get the picture returned from camera");
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
} else {
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
}
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
PreferenceManager.getDefaultSharedPreferences(activity)
.edit()
.remove(KEY_LAST_CAMERA_PHOTO)
.remove(KEY_PHOTO_URI)
.apply();
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
}
private static void onVideoReturnedFromCamera(Activity activity, @NonNull FilePicker.Callbacks callbacks) {
try {
String lastVideoUri = PreferenceManager.getDefaultSharedPreferences(activity).getString(KEY_VIDEO_URI, null);
if (!TextUtils.isEmpty(lastVideoUri)) {
revokeWritePermission(activity, Uri.parse(lastVideoUri));
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
UploadableFile photoFile = FilePicker.takenCameraVideo(activity);
List<UploadableFile> files = new ArrayList<>();
files.add(photoFile);
if (photoFile == null) {
Exception e = new IllegalStateException("Unable to get the video returned from camera");
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
} else {
if (configuration(activity).shouldCopyTakenPhotosToPublicGalleryAppFolder()) {
PickedFiles.copyFilesInSeparateThread(activity, singleFileList(photoFile));
}
callbacks.onImagesPicked(files, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
}
PreferenceManager.getDefaultSharedPreferences(activity)
.edit()
.remove(KEY_LAST_CAMERA_VIDEO)
.remove(KEY_VIDEO_URI)
.apply();
} catch (Exception e) {
e.printStackTrace();
callbacks.onImagePickerError(e, FilePicker.ImageSource.CAMERA_VIDEO, restoreType(activity));
} else {
callbacks.onCanceled(FilePicker.ImageSource.CAMERA_IMAGE, restoreType(activity));
}
}
@ -406,4 +348,8 @@ public class FilePicker implements Constants {
void onCanceled(FilePicker.ImageSource source, int type);
}
public interface HandleActivityResult{
void onHandleActivityResult(FilePicker.Callbacks callbacks);
}
}

View file

@ -1,13 +1,10 @@
package fr.free.nrw.commons.media;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_NEEDING_CATEGORIES;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_UNCATEGORISED;
import static fr.free.nrw.commons.description.EditDescriptionConstants.LIST_OF_DESCRIPTION_AND_CAPTION;
import static fr.free.nrw.commons.description.EditDescriptionConstants.UPDATED_WIKITEXT;
import static fr.free.nrw.commons.description.EditDescriptionConstants.WIKITEXT;
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
@ -112,8 +109,6 @@ import timber.log.Timber;
public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
CategoryEditHelper.Callback {
private static final int REQUEST_CODE = 1001;
private static final int REQUEST_CODE_EDIT_DESCRIPTION = 1002;
private static final String IMAGE_BACKGROUND_COLOR = "image_background_color";
static final int DEFAULT_IMAGE_BACKGROUND_COLOR = 0;
@ -277,6 +272,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
if (!sessionManager.isUserLoggedIn()) {
binding.categoryEditButton.setVisibility(GONE);
binding.descriptionEdit.setVisibility(GONE);
binding.depictionsEditButton.setVisibility(GONE);
} else {
binding.categoryEditButton.setVisibility(VISIBLE);
binding.descriptionEdit.setVisibility(VISIBLE);
binding.depictionsEditButton.setVisibility(VISIBLE);
}
if(applicationKvStore.getBoolean("login_skipped")){
@ -405,7 +406,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
}
);
binding.progressBarEdit.setVisibility(GONE);
binding.descriptionEdit.setVisibility(VISIBLE);
}
@Override
@ -605,8 +605,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
// Check if the presented category is about need of category
if (categoriesPresent) {
for (String category : media.getCategories()) {
if (category.toLowerCase().contains(CATEGORY_NEEDING_CATEGORIES) ||
category.toLowerCase().contains(CATEGORY_UNCATEGORISED)) {
if (category.toLowerCase(Locale.ROOT).contains(CATEGORY_NEEDING_CATEGORIES) ||
category.toLowerCase(Locale.ROOT).contains(CATEGORY_UNCATEGORISED)) {
categoriesPresent = false;
}
break;
@ -683,7 +683,9 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
// Stick in a filler element.
allCategories.add(getString(R.string.detail_panel_cats_none));
}
binding.categoryEditButton.setVisibility(VISIBLE);
if(sessionManager.isUserLoggedIn()) {
binding.categoryEditButton.setVisibility(VISIBLE);
}
rebuildCatList(allCategories);
}
@ -1065,81 +1067,6 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment implements
return captionList;
}
/**
* Get the result from another activity and act accordingly.
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(final int requestCode, final int resultCode,
@Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_OK) {
final String updatedWikiText = data.getStringExtra(UPDATED_WIKITEXT);
try {
compositeDisposable.add(descriptionEditHelper.addDescription(getContext(), media,
updatedWikiText)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
Timber.d("Descriptions are added.");
}));
} catch (Exception e) {
if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) {
final String username = sessionManager.getUserName();
final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener(
getActivity(),
requireActivity().getString(R.string.invalid_login_message),
username
);
CommonsApplication.getInstance().clearApplicationData(
requireActivity(), logoutListener);
}
}
final ArrayList<UploadMediaDetail> uploadMediaDetails
= data.getParcelableArrayListExtra(LIST_OF_DESCRIPTION_AND_CAPTION);
LinkedHashMap<String, String> updatedCaptions = new LinkedHashMap<>();
for (UploadMediaDetail mediaDetail:
uploadMediaDetails) {
try {
compositeDisposable.add(descriptionEditHelper.addCaption(getContext(), media,
mediaDetail.getLanguageCode(), mediaDetail.getCaptionText())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(s -> {
updateCaptions(mediaDetail, updatedCaptions);
Timber.d("Caption is added.");
}));
} catch (Exception e) {
if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) {
final String username = sessionManager.getUserName();
final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener(
getActivity(),
requireActivity().getString(R.string.invalid_login_message),
username
);
CommonsApplication.getInstance().clearApplicationData(
requireActivity(), logoutListener);
}
}
}
binding.progressBarEdit.setVisibility(GONE);
binding.descriptionEdit.setVisibility(VISIBLE);
} else if (requestCode == REQUEST_CODE_EDIT_DESCRIPTION && resultCode == RESULT_CANCELED) {
binding.progressBarEdit.setVisibility(GONE);
binding.descriptionEdit.setVisibility(VISIBLE);
}
}
/**
* Adds caption to the map and updates captions
* @param mediaDetail UploadMediaDetail

View file

@ -219,7 +219,7 @@ class ZoomableActivity : BaseActivity() {
onSwipe()
}
}
binding.zoomProgressBar?.let {
binding.zoomProgressBar.let {
it.visibility = if (result.status is CallbackStatus.FETCHING) View.VISIBLE else View.GONE
}
}
@ -234,7 +234,7 @@ class ZoomableActivity : BaseActivity() {
sharedPreferences.getBoolean(ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true)
if (!images.isNullOrEmpty()) {
binding.zoomable!!.setOnTouchListener(
binding.zoomable.setOnTouchListener(
object : OnSwipeTouchListener(this) {
// Swipe left to view next image in the folder. (if available)
override fun onSwipeLeft() {
@ -271,7 +271,7 @@ class ZoomableActivity : BaseActivity() {
* Handles down swipe action
*/
private fun onDownSwiped() {
if (binding.zoomable?.zoomableController?.isIdentity == false) {
if (binding.zoomable.zoomableController?.isIdentity == false) {
return
}
@ -341,7 +341,7 @@ class ZoomableActivity : BaseActivity() {
* Handles up swipe action
*/
private fun onUpSwiped() {
if (binding.zoomable?.zoomableController?.isIdentity == false) {
if (binding.zoomable.zoomableController?.isIdentity == false) {
return
}
@ -414,7 +414,7 @@ class ZoomableActivity : BaseActivity() {
* Handles right swipe action
*/
private fun onRightSwiped(showAlreadyActionedImages: Boolean) {
if (binding.zoomable?.zoomableController?.isIdentity == false) {
if (binding.zoomable.zoomableController?.isIdentity == false) {
return
}
@ -451,7 +451,7 @@ class ZoomableActivity : BaseActivity() {
* Handles left swipe action
*/
private fun onLeftSwiped(showAlreadyActionedImages: Boolean) {
if (binding.zoomable?.zoomableController?.isIdentity == false) {
if (binding.zoomable.zoomableController?.isIdentity == false) {
return
}
@ -646,7 +646,7 @@ class ZoomableActivity : BaseActivity() {
.setProgressBarImage(ProgressBarDrawable())
.setProgressBarImageScaleType(ScalingUtils.ScaleType.FIT_CENTER)
.build()
with(binding.zoomable!!) {
with(binding.zoomable) {
setHierarchy(hierarchy)
setAllowTouchInterceptionWhileZoomed(true)
setIsLongpressEnabled(false)
@ -658,10 +658,10 @@ class ZoomableActivity : BaseActivity() {
.setUri(imageUri)
.setControllerListener(loadingListener)
.build()
binding.zoomable!!.controller = controller
binding.zoomable.controller = controller
if (photoBackgroundColor != null) {
binding.zoomable!!.setBackgroundColor(photoBackgroundColor!!)
binding.zoomable.setBackgroundColor(photoBackgroundColor!!)
}
if (!images.isNullOrEmpty()) {

View file

@ -285,7 +285,7 @@ public class OkHttpJsonApiClient {
throws Exception {
Timber.d("Fetching nearby items at radius %s", radius);
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
Timber.d("CUSTOM_SPARQL: %s", String.valueOf(customQuery != null));
final String wikidataQuery;
if (customQuery != null) {
wikidataQuery = customQuery;
@ -344,7 +344,7 @@ public class OkHttpJsonApiClient {
final boolean shouldQueryForMonuments, final String customQuery)
throws Exception {
Timber.d("CUSTOM_SPARQL%s", String.valueOf(customQuery != null));
Timber.d("CUSTOM_SPARQL: %s", String.valueOf(customQuery != null));
final String wikidataQuery;
if (customQuery != null) {

View file

@ -17,6 +17,7 @@ import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import fr.free.nrw.commons.R;
import java.util.Locale;
public class NearbyFilterSearchRecyclerViewAdapter
extends RecyclerView.Adapter<NearbyFilterSearchRecyclerViewAdapter.RecyclerViewHolder>
@ -121,11 +122,11 @@ public class NearbyFilterSearchRecyclerViewAdapter
results.count = labels.size();
results.values = labels;
} else {
constraint = constraint.toString().toLowerCase();
constraint = constraint.toString().toLowerCase(Locale.ROOT);
for (Label label : labels) {
String data = label.toString();
if (data.toLowerCase().startsWith(constraint.toString())) {
if (data.toLowerCase(Locale.ROOT).startsWith(constraint.toString())) {
filteredArrayList.add(Label.fromText(label.getText()));
}
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby
import android.content.Intent
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
@ -17,9 +18,9 @@ import fr.free.nrw.commons.databinding.ItemPlaceBinding
fun placeAdapterDelegate(
bookmarkLocationDao: BookmarkLocationsDao,
onItemClick: ((Place) -> Unit)? = null,
onCameraClicked: (Place, ActivityResultLauncher<Array<String>>) -> Unit,
onCameraClicked: (Place, ActivityResultLauncher<Array<String>>, ActivityResultLauncher<Intent>) -> Unit,
onCameraLongPressed: () -> Boolean,
onGalleryClicked: (Place) -> Unit,
onGalleryClicked: (Place, ActivityResultLauncher<Intent>) -> Unit,
onGalleryLongPressed: () -> Boolean,
onBookmarkClicked: (Place, Boolean) -> Unit,
onBookmarkLongPressed: () -> Boolean,
@ -28,6 +29,8 @@ fun placeAdapterDelegate(
onDirectionsClicked: (Place) -> Unit,
onDirectionsLongPressed: () -> Boolean,
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>,
cameraPickLauncherForResult: ActivityResultLauncher<Intent>,
galleryPickLauncherForResult: ActivityResultLauncher<Intent>
) = adapterDelegateViewBinding<Place, Place, ItemPlaceBinding>({ layoutInflater, parent ->
ItemPlaceBinding.inflate(layoutInflater, parent, false)
}) {
@ -44,10 +47,10 @@ fun placeAdapterDelegate(
onItemClick?.invoke(item)
}
}
nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher) }
nearbyButtonLayout.cameraButton.setOnClickListener { onCameraClicked(item, inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult) }
nearbyButtonLayout.cameraButton.setOnLongClickListener { onCameraLongPressed() }
nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item) }
nearbyButtonLayout.galleryButton.setOnClickListener { onGalleryClicked(item, galleryPickLauncherForResult) }
nearbyButtonLayout.galleryButton.setOnLongClickListener { onGalleryLongPressed() }
bookmarkButtonImage.setOnClickListener {
val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item)

View file

@ -37,8 +37,21 @@ public abstract class PlaceDao {
*/
public Completable save(final Place place) {
return Completable
.fromAction(() -> {
saveSynchronous(place);
});
.fromAction(() -> saveSynchronous(place));
}
/**
* Deletes all Place objects from the database.
*/
@Query("DELETE FROM place")
public abstract void deleteAllSynchronous();
/**
* Deletes all Place objects from the database.
*
* @return A Completable that completes once the deletion operation is done.
*/
public Completable deleteAll() {
return Completable.fromAction(this::deleteAllSynchronous);
}
}

View file

@ -35,4 +35,8 @@ public class PlacesLocalDataSource {
public Completable savePlace(Place place) {
return placeDao.save(place);
}
public Completable clearCache() {
return placeDao.deleteAll();
}
}

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.location.LatLng;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import javax.inject.Inject;
/**
@ -38,4 +39,13 @@ public class PlacesRepository {
return localDataSource.fetchPlace(entityID);
}
/**
* Clears the Nearby cache on an IO thread.
*
* @return A Completable that completes once the cache has been successfully cleared.
*/
public Completable clearCache() {
return localDataSource.clearCache()
.subscribeOn(Schedulers.io()); // Ensure it runs on IO thread
}
}

View file

@ -87,7 +87,7 @@ class WikidataFeedback : BaseActivity() {
lat,
lng,
)
} as Callable<SingleSource<Boolean?>>,
},
).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ aBoolean: Boolean? ->

View file

@ -28,14 +28,14 @@ class CommonPlaceClickActions
private val activity: Activity,
private val contributionController: ContributionController,
) {
fun onCameraClicked(): (Place, ActivityResultLauncher<Array<String>>) -> Unit =
{ place, launcher ->
fun onCameraClicked(): (Place, ActivityResultLauncher<Array<String>>, ActivityResultLauncher<Intent>) -> Unit =
{ place, launcher, resultLauncher ->
if (applicationKvStore.getBoolean("login_skipped", false)) {
showLoginDialog()
} else {
Timber.d("Camera button tapped. Image title: ${place.getName()}Image desc: ${place.longDescription}")
storeSharedPrefs(place)
contributionController.initiateCameraPick(activity, launcher)
contributionController.initiateCameraPick(activity, launcher, resultLauncher)
}
}
@ -72,14 +72,14 @@ class CommonPlaceClickActions
true
}
fun onGalleryClicked(): (Place) -> Unit =
{
fun onGalleryClicked(): (Place, ActivityResultLauncher<Intent>) -> Unit =
{place, galleryPickLauncherForResult ->
if (applicationKvStore.getBoolean("login_skipped", false)) {
showLoginDialog()
} else {
Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}")
storeSharedPrefs(it)
contributionController.initiateGalleryPick(activity, false)
Timber.d("Gallery button tapped. Image title: ${place.getName()}Image desc: ${place.getLongDescription()}")
storeSharedPrefs(place)
contributionController.initiateGalleryPick(activity, galleryPickLauncherForResult, false)
}
}

View file

@ -43,15 +43,18 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog.Builder;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
@ -105,6 +108,7 @@ import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.SystemThemeUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@ -218,9 +222,36 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private LatLng updatedLatLng;
private boolean searchable;
private ConstraintLayout nearbyLegend;
private GridLayoutManager gridLayoutManager;
private List<BottomSheetItem> dataList;
private BottomSheetAdapter bottomSheetAdapter;
private final ActivityResultLauncher<Intent> galleryPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> customSelectorLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks);
});
});
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
controller.handleActivityResultWithCallback(requireActivity(), callbacks -> {
controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(
new RequestMultiplePermissions(),
new ActivityResultCallback<Map<String, Boolean>>() {
@ -236,7 +267,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
} else {
if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
controller.handleShowRationaleFlowCameraLocation(getActivity(),
inAppCameraLocationPermissionLauncher);
inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
} else {
controller.locationPermissionCallback.onLocationPermissionDenied(
getActivity().getString(
@ -302,6 +333,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
progressDialog.setCancelable(false);
progressDialog.setMessage("Saving in progress...");
setHasOptionsMenu(true);
// Inflate the layout for this fragment
return view;
@ -311,9 +343,21 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
public void onCreateOptionsMenu(@NonNull final Menu menu,
@NonNull final MenuInflater inflater) {
inflater.inflate(R.menu.nearby_fragment_menu, menu);
MenuItem refreshButton = menu.findItem(R.id.item_refresh);
MenuItem listMenu = menu.findItem(R.id.list_sheet);
MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx);
MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml);
refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
try {
emptyCache();
} catch (Exception e) {
throw new RuntimeException(e);
}
return false;
}
});
listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
@ -362,6 +406,16 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager,
this);
// Set up the floating activity button to toggle the visibility of the legend
binding.fabLegend.setOnClickListener(v -> {
if (binding.nearbyLegendLayout.getRoot().getVisibility() == View.VISIBLE) {
binding.nearbyLegendLayout.getRoot().setVisibility(View.GONE);
} else {
binding.nearbyLegendLayout.getRoot().setVisibility(View.VISIBLE);
}
});
presenter.attachView(this);
isPermissionDenied = false;
recenterToUserLocation = false;
@ -555,7 +609,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
return Unit.INSTANCE;
},
commonPlaceClickActions,
inAppCameraLocationPermissionLauncher
inAppCameraLocationPermissionLauncher,
galleryPickLauncherForResult,
cameraPickLauncherForResult
);
binding.bottomSheetNearby.rvNearbyList.setAdapter(adapter);
}
@ -1115,6 +1171,48 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
/**
* Reloads the Nearby map
* Clears all location markers, refreshes them, reinserts them into the map.
*
*/
private void reloadMap() {
clearAllMarkers(); // Clear the list of markers
binding.map.getController().setZoom(ZOOM_LEVEL); // Reset the zoom level
binding.map.getController().setCenter(lastMapFocus); // Recenter the focus
if (locationPermissionsHelper.checkLocationPermission(getActivity())) {
locationPermissionGranted(); // Reload map with user's location
} else {
startMapWithoutPermission(); // Reload map without user's location
}
binding.map.invalidate(); // Invalidate the map
presenter.updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED); // Restart the map
Timber.d("Reloaded Map Successfully");
}
/**
* Clears the Nearby local cache and then calls for the map to be reloaded
*
*/
private void emptyCache() {
// reload the map once the cache is cleared
compositeDisposable.add(
placesRepository.clearCache()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.andThen(Completable.fromAction(this::reloadMap))
.subscribe(
() -> {
Timber.d("Nearby Cache cleared successfully.");
},
throwable -> {
Timber.e(throwable, "Failed to clear the Nearby Cache");
}
)
);
}
private void savePlacesAsKML() {
final Observable<String> savePlacesObservable = Observable
.fromCallable(() -> nearbyController
@ -2186,7 +2284,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (binding.fabCamera.isShown()) {
Timber.d("Camera button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace);
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher);
controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
}
});
@ -2195,6 +2293,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace);
controller.initiateGalleryPick(getActivity(),
galleryPickLauncherForResult,
false);
}
});
@ -2203,7 +2302,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (binding.fabCustomGallery.isShown()) {
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace);
controller.initiateCustomGalleryPickWithPermission(getActivity());
controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult);
}
});
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.nearby.fragments
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
import fr.free.nrw.commons.nearby.Place
@ -12,6 +13,8 @@ class PlaceAdapter(
onBookmarkClicked: (Place, Boolean) -> Unit,
commonPlaceClickActions: CommonPlaceClickActions,
inAppCameraLocationPermissionLauncher: ActivityResultLauncher<Array<String>>,
galleryPickLauncherForResult: ActivityResultLauncher<Intent>,
cameraPickLauncherForResult: ActivityResultLauncher<Intent>
) : BaseDelegateAdapter<Place>(
placeAdapterDelegate(
bookmarkLocationsDao,
@ -27,6 +30,8 @@ class PlaceAdapter(
commonPlaceClickActions.onDirectionsClicked(),
commonPlaceClickActions.onDirectionsLongPressed(),
inAppCameraLocationPermissionLauncher,
cameraPickLauncherForResult,
galleryPickLauncherForResult
),
areItemsTheSame = { oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId },
)

View file

@ -32,6 +32,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
/**
@ -139,14 +140,14 @@ public class ProfileActivity extends BaseActivity {
leaderboardFragment.setArguments(leaderBoardBundle);
fragmentList.add(leaderboardFragment);
titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase());
titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase(Locale.ROOT));
contributionsFragment = new ContributionsFragment();
Bundle contributionsListBundle = new Bundle();
contributionsListBundle.putString(KEY_USERNAME, userName);
contributionsFragment.setArguments(contributionsListBundle);
fragmentList.add(contributionsFragment);
titleList.add(getString(R.string.contributions_fragment).toUpperCase());
titleList.add(getString(R.string.contributions_fragment).toUpperCase(Locale.ROOT));
viewPagerAdapter.setTabData(fragmentList, titleList);
viewPagerAdapter.notifyDataSetChanged();

View file

@ -27,6 +27,7 @@ import fr.free.nrw.commons.profile.ProfileActivity;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Locale;
import java.util.Objects;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
@ -361,7 +362,7 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment {
+ levelInfo.getMaxUniqueImages());
binding.imageFeatured.setText(String.valueOf(achievements.getFeaturedImages()));
binding.qualityImages.setText(String.valueOf(achievements.getQualityImages()));
String levelUpInfoString = getString(R.string.level).toUpperCase();
String levelUpInfoString = getString(R.string.level).toUpperCase(Locale.ROOT);
levelUpInfoString += " " + levelInfo.getLevelNumber();
binding.achievementLevel.setText(levelUpInfoString);
binding.achievementBadgeImage.setImageDrawable(VectorDrawableCompat.create(getResources(), R.drawable.badge,

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons.recentlanguages;
import android.annotation.SuppressLint;
import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
@ -117,6 +118,7 @@ public class RecentLanguagesDao {
* @return Language object
*/
@NonNull
@SuppressLint("Range")
Language fromCursor(final Cursor cursor) {
// Hardcoding column positions!
final String languageName = cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME));

View file

@ -25,6 +25,7 @@ import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Locale;
import javax.inject.Inject;
public class ReviewActivity extends BaseActivity {
@ -241,7 +242,7 @@ public class ReviewActivity extends BaseActivity {
public void showSkipImageInfo(){
DialogUtil.showAlertDialog(ReviewActivity.this,
getString(R.string.skip_image).toUpperCase(),
getString(R.string.skip_image).toUpperCase(Locale.ROOT),
getString(R.string.skip_image_explanation),
getString(android.R.string.ok),
"",

View file

@ -21,6 +21,7 @@ import android.widget.TextView;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
@ -85,6 +86,15 @@ public class SettingsFragment extends PreferenceFragmentCompat {
private View separator;
private ListView languageHistoryListView;
private static final String GET_CONTENT_PICKER_HELP_URL = "https://commons-app.github.io/docs.html#get-content";
private final ActivityResultLauncher<Intent> cameraPickLauncherForResult =
registerForActivityResult(new StartActivityForResult(),
result -> {
contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
});
});
private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() {
@Override
public void onActivityResult(Map<String, Boolean> result) {
@ -93,7 +103,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
areAllGranted = areAllGranted && b;
}
if (!areAllGranted && shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) {
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher);
contributionController.handleShowRationaleFlowCameraLocation(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult);
}
}
});

View file

@ -63,7 +63,7 @@ class FailedUploadsFragment :
}
if (StringUtils.isEmpty(userName)) {
userName = sessionManager!!.getUserName()
userName = sessionManager.getUserName()
}
}
@ -96,8 +96,8 @@ class FailedUploadsFragment :
fun initRecyclerView() {
binding.failedUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context))
binding.failedUploadsRecyclerView.adapter = adapter
pendingUploadsPresenter!!.getFailedContributions()
pendingUploadsPresenter!!.failedContributionList.observe(
pendingUploadsPresenter.getFailedContributions()
pendingUploadsPresenter.failedContributionList.observe(
viewLifecycleOwner,
) { list: PagedList<Contribution?> ->
adapter.submitList(list)

View file

@ -19,6 +19,7 @@ import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import timber.log.Timber;
public class FileUtils {
@ -139,7 +140,7 @@ public class FileUtils {
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
.toString());
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
fileExtension.toLowerCase());
fileExtension.toLowerCase(Locale.getDefault()));
}
return mimeType;
}

View file

@ -74,8 +74,8 @@ class PendingUploadsFragment :
fun initRecyclerView() {
binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context))
binding.pendingUploadsRecyclerView.adapter = adapter
pendingUploadsPresenter!!.setup()
pendingUploadsPresenter!!.totalContributionList.observe(
pendingUploadsPresenter.setup()
pendingUploadsPresenter.totalContributionList.observe(
viewLifecycleOwner,
) { list: PagedList<Contribution?> ->
contributionsSize = list.size

View file

@ -320,6 +320,14 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
finish();
}
/**
* go to the uploadProgress activity to check the status of uploading
*/
@Override
public void goToUploadProgressActivity() {
startActivity(new Intent(this, UploadProgressActivity.class));
}
/**
* Show/Hide the progress dialog
*/
@ -433,14 +441,6 @@ public class UploadActivity extends BaseActivity implements UploadContract.View,
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS) {
//TODO: Confirm if handling manual permission enabled is required
}
}
/**
* Sets the flag indicating whether the upload is of a specific place.
*

View file

@ -18,6 +18,13 @@ public interface UploadContract {
void returnToMainActivity();
/**
* When submission successful, go to the loadProgressActivity to hint the user this
* submission is valid. And the user will see the upload progress in this activity;
* Fixes: <a href="https://github.com/commons-app/apps-android-commons/issues/5846">Issue</a>
*/
void goToUploadProgressActivity();
void askUserToLogIn();
/**

View file

@ -20,6 +20,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
@ -57,27 +58,29 @@ public class UploadMediaDetailAdapter extends
private int currentPosition;
private Fragment fragment;
private Activity activity;
private final ActivityResultLauncher<Intent> voiceInputResultLauncher;
private SelectedVoiceIcon selectedVoiceIcon;
private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213;
private RowItemDescriptionBinding binding;
public UploadMediaDetailAdapter(Fragment fragment, String savedLanguageValue,
RecentLanguagesDao recentLanguagesDao) {
RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
uploadMediaDetails = new ArrayList<>();
selectedLanguages = new HashMap<>();
this.savedLanguageValue = savedLanguageValue;
this.recentLanguagesDao = recentLanguagesDao;
this.fragment = fragment;
this.voiceInputResultLauncher = voiceInputResultLauncher;
}
public UploadMediaDetailAdapter(Activity activity, final String savedLanguageValue,
List<UploadMediaDetail> uploadMediaDetails, RecentLanguagesDao recentLanguagesDao) {
List<UploadMediaDetail> uploadMediaDetails, RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
this.uploadMediaDetails = uploadMediaDetails;
selectedLanguages = new HashMap<>();
this.savedLanguageValue = savedLanguageValue;
this.recentLanguagesDao = recentLanguagesDao;
this.activity = activity;
this.voiceInputResultLauncher = voiceInputResultLauncher;
}
public void setCallback(Callback callback) {
@ -150,11 +153,7 @@ public class UploadMediaDetailAdapter extends
);
try {
if (activity == null) {
fragment.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT);
} else {
activity.startActivityForResult(intent, REQUEST_CODE_FOR_VOICE_INPUT);
}
voiceInputResultLauncher.launch(intent);
} catch (Exception e) {
Timber.e(e.getMessage());
}
@ -407,7 +406,7 @@ public class UploadMediaDetailAdapter extends
recentLanguagesDao
.addRecentLanguage(new Language(languageName, languageCode));
selectedLanguages.remove(position);
selectedLanguages.clear();
selectedLanguages.put(position, languageCode);
((LanguagesAdapter) adapterView
.getAdapter()).setSelectedLangCode(languageCode);
@ -497,7 +496,7 @@ public class UploadMediaDetailAdapter extends
}
recentLanguagesDao.addRecentLanguage(new Language(languageName, languageCode));
selectedLanguages.remove(position);
selectedLanguages.clear();
selectedLanguages.put(position, languageCode);
((RecentLanguagesAdapter) adapterView
.getAdapter()).setSelectedLangCode(languageCode);

View file

@ -123,6 +123,9 @@ public class UploadPresenter implements UploadContract.UserActionListener {
view.returnToMainActivity();
compositeDisposable.clear();
Timber.e("failed to upload: " + e.getMessage());
//is submission error, not need to go to the uploadActivity
//not start the uploading progress
}
@Override
@ -131,6 +134,10 @@ public class UploadPresenter implements UploadContract.UserActionListener {
repository.cleanup();
view.returnToMainActivity();
compositeDisposable.clear();
//after finish the uploadActivity, if successful,
//directly go to the upload progress activity
view.goToUploadProgressActivity();
}
});
} else {

View file

@ -10,15 +10,13 @@ abstract class BaseDelegateAdapter<T>(
areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new },
) : AsyncListDifferDelegationAdapter<T>(
object : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(
oldItem: T,
newItem: T,
) = areItemsTheSame(oldItem, newItem)
override fun areItemsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
return areItemsTheSame(oldItem, newItem)
}
override fun areContentsTheSame(
oldItem: T,
newItem: T,
) = areContentsTheSame(oldItem, newItem)
override fun areContentsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
return areContentsTheSame(oldItem, newItem)
}
},
*delegates,
) {

View file

@ -372,7 +372,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
return false;
});
Objects.requireNonNull(getView()).setFocusableInTouchMode(true);
requireView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
@ -387,7 +387,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
});
Objects.requireNonNull(
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
((AppCompatActivity) requireActivity()).getSupportActionBar())
.hide();
if (getParentFragment().getParentFragment().getParentFragment()
@ -407,7 +407,7 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
super.onStop();
if (media != null) {
Objects.requireNonNull(
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
((AppCompatActivity) requireActivity()).getSupportActionBar())
.show();
}
}

View file

@ -36,7 +36,7 @@ abstract class DepictsDao {
/**
* Gets all Depicts objects from the database, ordered by lastUsed in descending order.
*
* @return A list of Depicts objects.
* @return Deferred list of Depicts objects.
*/
fun depictsList(): Deferred<List<Depicts>> =
CoroutineScope(Dispatchers.IO).async {
@ -48,7 +48,7 @@ abstract class DepictsDao {
*
* @param depictedItem The Depicts object to insert.
*/
private fun insertDepict(depictedItem: Depicts) =
fun insertDepict(depictedItem: Depicts) =
CoroutineScope(Dispatchers.IO).launch {
insert(depictedItem)
}
@ -59,7 +59,7 @@ abstract class DepictsDao {
* @param n The number of depicts to delete.
* @return A list of Depicts objects to delete.
*/
private suspend fun depictsForDeletion(n: Int): Deferred<List<Depicts>> =
fun depictsForDeletion(n: Int): Deferred<List<Depicts>> =
CoroutineScope(Dispatchers.IO).async {
getDepictsForDeletion(n)
}
@ -69,7 +69,7 @@ abstract class DepictsDao {
*
* @param depicts The Depicts object to delete.
*/
private suspend fun deleteDepicts(depicts: Depicts) =
fun deleteDepicts(depicts: Depicts) =
CoroutineScope(Dispatchers.IO).launch {
delete(depicts)
}

View file

@ -398,7 +398,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
return false;
});
Objects.requireNonNull(getView()).setFocusableInTouchMode(true);
requireView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener((v, keyCode, event) -> {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
@ -411,7 +411,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
});
Objects.requireNonNull(
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
((AppCompatActivity) requireActivity()).getSupportActionBar())
.hide();
if (getParentFragment().getParentFragment().getParentFragment()
@ -431,7 +431,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
super.onStop();
if (media != null) {
Objects.requireNonNull(
((AppCompatActivity) Objects.requireNonNull(getActivity())).getSupportActionBar())
((AppCompatActivity) requireActivity()).getSupportActionBar())
.show();
}
}

View file

@ -18,6 +18,9 @@ import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;
@ -58,9 +61,24 @@ import timber.log.Timber;
public class UploadMediaDetailFragment extends UploadBaseFragment implements
UploadMediaDetailsContract.View, UploadMediaDetailAdapter.EventListener {
private static final int REQUEST_CODE = 1211;
private static final int REQUEST_CODE_FOR_EDIT_ACTIVITY = 1212;
private static final int REQUEST_CODE_FOR_VOICE_INPUT = 1213;
private UploadMediaDetailAdapter uploadMediaDetailAdapter;
private final ActivityResultLauncher<Intent> startForResult = registerForActivityResult(
new StartActivityForResult(), result -> {
onCameraPosition(result);
});
private final ActivityResultLauncher<Intent> startForEditActivityResult = registerForActivityResult(
new StartActivityForResult(), result -> {
onEditActivityResult(result);
}
);
private final ActivityResultLauncher<Intent> voiceInputResultLauncher = registerForActivityResult(
new StartActivityForResult(), result -> {
onVoiceInput(result);
}
);
public static Activity activity ;
@ -84,8 +102,6 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
private boolean hasUserRemovedLocation;
private UploadMediaDetailAdapter uploadMediaDetailAdapter;
@Inject
UploadMediaDetailsContract.UserActionListener presenter;
@ -279,7 +295,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
*/
private void initRecyclerView() {
uploadMediaDetailAdapter = new UploadMediaDetailAdapter(this,
defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao);
defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, ""), recentLanguagesDao, voiceInputResultLauncher);
uploadMediaDetailAdapter.setCallback(this::showInfoAlert);
uploadMediaDetailAdapter.setEventListener(this);
binding.rvDescriptions.setLayoutManager(new LinearLayoutManager(getContext()));
@ -593,14 +609,14 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
* This method is called to start the image editing activity for a specific UploadItem.
* It sets the UploadItem as the currently editable item, creates an intent to launch the
* EditActivity, and passes the image file path as an extra in the intent. The activity
* is started with a request code, allowing the result to be handled in onActivityResult.
* is started using resultLauncher that handles the result in respective callback.
*/
@Override
public void showEditActivity(UploadItem uploadItem) {
editableUploadItem = uploadItem;
Intent intent = new Intent(getContext(), EditActivity.class);
intent.putExtra("image", uploadableFile.getFilePath().toString());
startActivityForResult(intent, REQUEST_CODE_FOR_EDIT_ACTIVITY);
startForEditActivityResult.launch(intent);
}
/**
@ -615,6 +631,8 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
double defaultLongitude = -122.431297;
double defaultZoom = 16.0;
final Intent locationPickerIntent;
/* Retrieve image location from EXIF if present or
check if user has provided location while using the in-app camera.
Use location of last UploadItem if none of them is available */
@ -624,10 +642,11 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
.getDecLatitude();
defaultLongitude = uploadItem.getGpsCoords().getDecLongitude();
defaultZoom = uploadItem.getGpsCoords().getZoomLevel();
startActivityForResult(new LocationPicker.IntentBuilder()
locationPickerIntent = new LocationPicker.IntentBuilder()
.defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom))
.activityKey("UploadActivity")
.build(getActivity()), REQUEST_CODE);
.build(getActivity());
} else {
if (defaultKvStore.getString(LAST_LOCATION) != null) {
final String[] locationLatLng
@ -638,27 +657,20 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
if (defaultKvStore.getString(LAST_ZOOM) != null) {
defaultZoom = Double.parseDouble(defaultKvStore.getString(LAST_ZOOM));
}
startActivityForResult(new LocationPicker.IntentBuilder()
locationPickerIntent = new LocationPicker.IntentBuilder()
.defaultLocation(new CameraPosition(defaultLatitude,defaultLongitude,defaultZoom))
.activityKey("NoLocationUploadActivity")
.build(getActivity()), REQUEST_CODE);
.build(getActivity());
}
startForResult.launch(locationPickerIntent);
}
/**
* Get the coordinates and update the existing coordinates.
* @param requestCode code of request
* @param resultCode code of result
* @param data intent
*/
@Override
public void onActivityResult(final int requestCode, final int resultCode,
@Nullable final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
private void onCameraPosition(ActivityResult result){
if (result.getResultCode() == RESULT_OK) {
assert data != null;
final CameraPosition cameraPosition = LocationPicker.getCameraPosition(data);
assert result.getData() != null;
final CameraPosition cameraPosition = LocationPicker.getCameraPosition(result.getData());
if (cameraPosition != null) {
@ -678,8 +690,21 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
removeLocation();
}
}
if (requestCode == REQUEST_CODE_FOR_EDIT_ACTIVITY && resultCode == RESULT_OK) {
String result = data.getStringExtra("editedImageFilePath");
}
private void onVoiceInput(ActivityResult result) {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
ArrayList<String> resultData = result.getData().getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
uploadMediaDetailAdapter.handleSpeechResult(resultData.get(0));
}else {
Timber.e("Error %s", result.getResultCode());
}
}
private void onEditActivityResult(ActivityResult result){
if (result.getResultCode() == RESULT_OK) {
String path = result.getData().getStringExtra("editedImageFilePath");
if (Objects.equals(result, "Error")) {
Timber.e("Error in rotating image");
@ -687,24 +712,15 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
}
try {
if (binding != null){
binding.backgroundImage.setImageURI(Uri.fromFile(new File(result)));
binding.backgroundImage.setImageURI(Uri.fromFile(new File(path)));
}
editableUploadItem.setContentUri(Uri.fromFile(new File(result)));
editableUploadItem.setContentUri(Uri.fromFile(new File(path)));
callback.changeThumbnail(indexOfFragment,
result);
path);
} catch (Exception e) {
Timber.e(e);
}
}
else if (requestCode == REQUEST_CODE_FOR_VOICE_INPUT) {
if (resultCode == RESULT_OK && data != null) {
ArrayList<String> result = data.getStringArrayListExtra(
RecognizerIntent.EXTRA_RESULTS);
uploadMediaDetailAdapter.handleSpeechResult(result.get(0));
}else {
Timber.e("Error %s", resultCode);
}
}
}
/**
@ -809,7 +825,7 @@ public class UploadMediaDetailFragment extends UploadBaseFragment implements
@Override
public void displayAddLocationDialog(final Runnable onSkipClicked) {
isMissingLocationDialog = true;
DialogUtil.showAlertDialog(Objects.requireNonNull(getActivity()),
DialogUtil.showAlertDialog(requireActivity(),
getString(R.string.no_location_found_title),
getString(R.string.no_location_found_message),
getString(R.string.add_location),

View file

@ -129,9 +129,9 @@ public class UploadMediaPresenter implements UserActionListener, SimilarImageInt
if (place.location != null) {
final String countryCode = reverseGeoCode(place.location);
if (countryCode != null && WLM_SUPPORTED_COUNTRIES
.contains(countryCode.toLowerCase())) {
.contains(countryCode.toLowerCase(Locale.ROOT))) {
uploadItem.setWLMUpload(true);
uploadItem.setCountryCode(countryCode.toLowerCase());
uploadItem.setCountryCode(countryCode.toLowerCase(Locale.ROOT));
}
}
}

View file

@ -41,8 +41,8 @@ import fr.free.nrw.commons.upload.UploadProgressActivity
import fr.free.nrw.commons.upload.UploadResult
import fr.free.nrw.commons.wikidata.WikidataEditService
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
@ -438,7 +438,7 @@ class UploadWorker(
username,
)
CommonsApplication
.getInstance()
.instance!!
.clearApplicationData(appContext, logoutListener)
}
}
@ -534,7 +534,7 @@ class UploadWorker(
contribution.contentUri?.let {
val imageSha1 = contribution.imageSHA1.toString()
val modifiedSha1 = fileUtilsWrapper.getSHA1(fileUtilsWrapper.getFileInputStream(contribution.localUri?.path))
MainScope().launch {
CoroutineScope(Dispatchers.IO).launch {
uploadedStatusDao.insertUploaded(
UploadedStatus(
imageSha1,

View file

@ -54,8 +54,7 @@ public class PermissionUtils {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
final Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent,
CommonsApplication.OPEN_APPLICATION_DETAIL_SETTINGS);
activity.startActivity(intent);
}
/**

View file

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/half_standard_height"
android:height="@dimen/half_standard_height"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<group
android:scaleX="1.0"
android:scaleY="1.0"
android:translateX="-0.0"
android:translateY="-0.0">
<path
android:fillColor="?attr/menu_item_tint"
android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</group>
</vector>

View file

@ -36,11 +36,11 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:contentDescription="@string/exit_location_picker"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_back_white" />
app:srcCompat="@drawable/ic_arrow_back_white"
app:tint="@color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -69,7 +69,7 @@
android:id="@+id/btn_edit_submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:text="@string/submit"
android:textColor="@android:color/white" />
</RelativeLayout>

View file

@ -31,7 +31,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginRight="50dp"
android:layout_marginEnd="50dp"
android:maxLines="2"
android:ellipsize="end"
/>
@ -58,6 +58,7 @@
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:padding="@dimen/standard_gap"
android:clickable="true"
android:background="@drawable/button_background_selector"
@ -69,8 +70,7 @@
android:layout_gravity="center_horizontal"
android:duplicateParentState="true"
app:srcCompat="@drawable/ic_directions_black_24dp"
android:tint="?attr/rowButtonColor"
/>
app:tint="?attr/rowButtonColor" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -89,6 +89,7 @@
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:padding="@dimen/standard_gap"
android:clickable="true"
android:background="@drawable/button_background_selector"
@ -118,6 +119,7 @@
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:padding="@dimen/standard_gap"
android:clickable="true"
android:background="@drawable/button_background_selector"
@ -153,8 +155,8 @@
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/large_height"
android:layout_marginRight="@dimen/standard_gap"
android:layout_marginStart="@dimen/large_height"
android:layout_marginEnd="@dimen/standard_gap"
android:layout_marginBottom="@dimen/standard_gap"
android:textSize="16sp" />

View file

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bookmarkButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_columnWeight="1"
android:background="@drawable/button_background_selector"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:padding="@dimen/standard_gap">
@ -14,7 +16,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:tint="?attr/rowButtonColor" />
app:tint="?attr/rowButtonColor" />
<TextView
android:id="@+id/buttonText"

View file

@ -28,7 +28,6 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar"
android:background="?attr/achievementBackground"
android:orientation="vertical">
@ -36,7 +35,6 @@
style="?android:textAppearanceLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:text="@string/level"
@ -48,13 +46,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_margin_vertical"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/black"
android:layout_marginVertical="@dimen/activity_margin_vertical" />
android:layout_marginVertical="@dimen/activity_margin_vertical"
app:tint="@color/black" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/badge_layout"
@ -108,7 +104,6 @@
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:id="@+id/images_upload_text_param"
android:layout_marginTop="@dimen/achievements_activity_margin_vertical"
@ -120,12 +115,10 @@
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/images_upload_text_param"
android:layout_toEndOf="@+id/images_upload_text_param"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"/>
android:layout_marginStart="@dimen/activity_margin_horizontal"
app:tint="@color/primaryLightColor" />
</LinearLayout>
@ -189,7 +182,6 @@
style="?android:textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:id="@+id/images_reverted_text"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:text="@string/image_reverts" />
@ -200,24 +192,19 @@
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/images_reverted_text"
android:layout_toEndOf="@+id/images_reverted_text"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"/>
android:layout_marginStart="@dimen/activity_margin_horizontal" app:tint="@color/primaryLightColor" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/achievements_revert_limit_message"
android:textSize="@dimen/small_text"
android:id="@+id/images_revert_limit_text"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_below="@id/images_reverted_info"/>
@ -278,7 +265,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/images_used_by_wiki_text"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/achievements_activity_margin_vertical"
android:text="@string/images_used_by_wiki" />
@ -289,12 +275,10 @@
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_toRightOf="@+id/images_used_by_wiki_text"
android:layout_toEndOf="@+id/images_used_by_wiki_text"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"/>
android:layout_marginStart="@dimen/activity_margin_horizontal"
app:tint="@color/primaryLightColor" />
</LinearLayout>
@ -353,7 +337,6 @@
android:layout_height="wrap_content"
android:text="@string/statistics"
style="?android:textAppearanceLarge"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_vertical"
android:textAllCaps="true"/>
@ -373,9 +356,7 @@
android:id="@+id/images_nearby_info"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_toStartOf="@+id/wikidata_edits"
android:layout_toLeftOf="@+id/wikidata_edits"
android:orientation="horizontal"
android:gravity="center_vertical">
@ -407,14 +388,13 @@
android:layout_height="@dimen/medium_height"
android:id="@+id/images_nearby_info_icon"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
android:layout_gravity="top"
app:layout_constraintLeft_toRightOf="@id/images_nearby_data"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor" />
app:tint="@color/primaryLightColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -423,16 +403,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/half_standard_height"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_centerVertical="true"
tools:text="2"
android:id="@+id/wikidata_edits"
android:layout_marginRight="@dimen/half_standard_height" />
/>
</RelativeLayout>
@ -451,9 +429,7 @@
android:id="@+id/images_featured_info"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_toStartOf="@+id/image_featured"
android:layout_toLeftOf="@+id/image_featured"
android:orientation="horizontal"
android:gravity="center_vertical">
@ -486,14 +462,13 @@
android:layout_height="@dimen/medium_height"
android:id="@+id/images_featured_info_icon"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
app:layout_constraintLeft_toRightOf="@id/images_featured_data"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_gravity="top"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor" />
app:tint="@color/primaryLightColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -501,16 +476,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_centerVertical="true"
tools:text="2"
android:id="@+id/image_featured"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/half_standard_height"
android:layout_marginRight="@dimen/half_standard_height" />
/>
</RelativeLayout>
@ -529,9 +502,7 @@
android:id="@+id/quality_images_info"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_toStartOf="@+id/quality_images"
android:layout_toLeftOf="@+id/quality_images"
android:orientation="horizontal"
android:gravity="center_vertical">
@ -564,14 +535,13 @@
android:layout_height="@dimen/medium_height"
android:id="@+id/quality_images_info_icon"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
app:layout_constraintLeft_toRightOf="@id/quality_images_data"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_gravity="top"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor" />
app:tint="@color/primaryLightColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -579,7 +549,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
@ -587,9 +556,8 @@
tools:text="2"
android:text="0"
android:id="@+id/quality_images"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/half_standard_height"
android:layout_marginRight="@dimen/half_standard_height" />
/>
</RelativeLayout>
@ -608,9 +576,7 @@
android:id="@+id/thanks_received_info"
android:layout_centerVertical="true"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_toStartOf="@+id/thanks_received"
android:layout_toLeftOf="@+id/thanks_received"
android:orientation="horizontal"
android:gravity="center_vertical">
@ -643,14 +609,13 @@
android:layout_height="@dimen/medium_height"
android:id="@+id/thanks_received_info_icon"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
android:layout_marginEnd="@dimen/activity_margin_horizontal"
app:layout_constraintLeft_toRightOf="@id/thanks_received_data"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_gravity="top"
app:srcCompat="@drawable/ic_info_outline_24dp"
android:tint="@color/primaryLightColor" />
app:tint="@color/primaryLightColor" />
</androidx.constraintlayout.widget.ConstraintLayout>
@ -658,16 +623,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="?android:textAppearanceMedium"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginStart="@dimen/activity_margin_horizontal"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_centerVertical="true"
tools:text="2"
android:id="@+id/thanks_received"
android:layout_marginEnd="@dimen/half_standard_height"
android:layout_marginRight="@dimen/half_standard_height" />
/>
</RelativeLayout>

View file

@ -124,6 +124,33 @@
app:srcCompat="@drawable/ic_my_location_black_24dp"
app:useCompatPadding="true" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_legend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/fab_recenter"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:clickable="true"
android:visibility="visible"
app:backgroundTint="@color/main_background_light"
app:elevation="@dimen/dimen_6"
app:fabSize="normal"
app:layout_anchorGravity="top|right|end"
app:srcCompat="@drawable/ic_info_outline_24dp"
app:useCompatPadding="true" />
<include
android:id="@+id/nearby_legend_layout"
layout="@layout/nearby_legend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/rl_container_wlm_month_message"
android:visibility="gone"
android:layout_marginTop="30dp"
android:layout_marginStart="5dp"
/>
</RelativeLayout>
<FrameLayout

View file

@ -11,8 +11,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/standard_gap"
android:tint="?attr/rowButtonColor"
app:srcCompat="@drawable/ic_round_star_border_24px" />
app:srcCompat="@drawable/ic_round_star_border_24px"
app:tint="?attr/rowButtonColor" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/icon"
@ -30,7 +30,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginRight="@dimen/standard_gap"
android:layout_marginTop="@dimen/standard_gap"
@ -43,8 +42,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/standard_gap"
android:layout_marginEnd="@dimen/standard_gap"
android:layout_marginTop="@dimen/large_gap"
/>
@ -54,11 +52,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@id/distance"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:layout_toEndOf="@id/icon"
android:layout_toLeftOf="@id/distance"
android:layout_toRightOf="@id/icon"
android:layout_toStartOf="@id/distance"
android:ellipsize="end"
android:maxLines="2"
@ -71,8 +66,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignEnd="@id/distance"
android:layout_alignLeft="@id/tvName"
android:layout_alignRight="@id/distance"
android:layout_alignStart="@id/tvName"
android:layout_below="@id/tvName"
android:layout_marginBottom="@dimen/standard_gap"

View file

@ -19,17 +19,14 @@
android:id="@+id/iv_campaign"
android:layout_width="@dimen/dimen_40"
android:layout_height="@dimen/dimen_40"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_campaign"
android:tint="?attr/card_item_color"
/>
app:tint="?attr/card_item_color" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:weightSum="4">
@ -37,15 +34,13 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginRight="@dimen/tiny_margin"
android:layout_centerInParent="true"
>
android:layout_marginStart="@dimen/standard_gap"
android:layout_marginEnd="@dimen/tiny_margin">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:textColor="?attr/card_item_color"
android:textStyle="bold"
tools:text="Campaign Title"
@ -55,7 +50,7 @@
android:id="@+id/tv_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:gravity="start"
android:paddingTop="@dimen/miniscule_margin"
android:textAlignment="textStart"
@ -69,7 +64,7 @@
android:id="@+id/tv_dates"
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:layout_weight="1"
android:paddingTop="@dimen/miniscule_margin"
android:text="@string/ends_on"

View file

@ -113,9 +113,9 @@
android:background="@android:color/transparent"
android:padding="@dimen/activity_margin_horizontal"
android:src="@drawable/ic_wikipedia"
android:tint="?attr/contributionsListTextSecondary"
android:text="@string/menu_cancel_upload"
android:visibility="visible" />
android:visibility="visible"
app:tint="?attr/contributionsListTextSecondary" />
</RelativeLayout>

View file

@ -14,7 +14,6 @@
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="@dimen/activity_margin_horizontal"
android:layout_marginTop="@dimen/activity_margin_horizontal"
android:layout_marginRight="@dimen/activity_margin_horizontal"
@ -30,34 +29,28 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/content_layout"
android:layout_centerInParent="true"
android:orientation="horizontal"
>
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_centerInParent="true"
/>
android:id="@+id/progressBar" />
<ImageView
android:id="@+id/nearby_icon"
android:layout_width="@dimen/dimen_40"
android:layout_height="@dimen/dimen_40"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginStart="@dimen/standard_gap"
android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_location_white_24dp"
android:tint="?attr/card_item_color"
/>
app:tint="?attr/card_item_color" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="horizontal"
android:orientation="horizontal"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:weightSum="4"
@ -68,8 +61,7 @@
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_centerInParent="true"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginLeft="@dimen/standard_gap"
android:layout_marginRight="@dimen/standard_gap"
tools:text="test distance"
android:textColor="?attr/card_item_color"

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#BFFFFFFF"
android:orientation="vertical">
<ImageView
android:id="@+id/imageRed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
app:layout_constraintBottom_toTopOf="@+id/imageGreen"
app:layout_constraintEnd_toEndOf="@+id/imageGreen"
app:layout_constraintStart_toStartOf="@+id/imageGreen"
app:srcCompat="@drawable/ic_custom_map_marker_red" />
<TextView
android:id="@+id/textRed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:text="@string/red_pin"
android:textColor="#F74D4D"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/imageRed"
app:layout_constraintStart_toEndOf="@+id/imageRed"
app:layout_constraintTop_toTopOf="@+id/imageRed" />
<ImageView
android:id="@+id/imageGreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
app:layout_constraintBottom_toTopOf="@+id/imageGrey"
app:layout_constraintEnd_toEndOf="@+id/imageGrey"
app:layout_constraintStart_toStartOf="@+id/imageGrey"
app:srcCompat="@drawable/ic_custom_map_marker_green" />
<TextView
android:id="@+id/textGreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:text="@string/green_pin"
android:textColor="#1F7123"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/imageGreen"
app:layout_constraintStart_toEndOf="@+id/imageGreen"
app:layout_constraintTop_toTopOf="@+id/imageGreen" />
<ImageView
android:id="@+id/imageGrey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_custom_map_marker_grey" />
<TextView
android:id="@+id/textGrey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/grey_pin"
android:textColor="#454547"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/imageGrey"
app:layout_constraintStart_toEndOf="@+id/imageGrey"
app:layout_constraintTop_toTopOf="@+id/imageGrey" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -17,6 +17,7 @@
android:layout_weight="1"
android:background="@drawable/button_background_selector"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:padding="@dimen/standard_gap">
@ -24,8 +25,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:tint="?attr/bookmarkButtonColor"
app:srcCompat="@drawable/ic_photo_camera_white_24dp" />
app:srcCompat="@drawable/ic_photo_camera_white_24dp"
app:tint="?attr/bookmarkButtonColor" />
<TextView
android:id="@+id/cameraButtonText"
@ -45,6 +46,7 @@
android:background="@drawable/button_background_selector"
android:clickable="true"
android:contentDescription="@string/nearby_row_image"
android:focusable="true"
android:orientation="vertical"
android:padding="@dimen/standard_gap">
@ -53,8 +55,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:duplicateParentState="true"
android:tint="?attr/bookmarkButtonColor"
app:srcCompat="@drawable/ic_photo_white_24dp" />
app:srcCompat="@drawable/ic_photo_white_24dp"
app:tint="?attr/bookmarkButtonColor" />
<TextView
android:id="@+id/galleryButtonText"
@ -72,6 +74,7 @@
android:layout_width="@dimen/dimen_0"
android:layout_height="wrap_content"
android:layout_weight="1"
android:focusable="true"
android:padding="@dimen/standard_gap"
android:clickable="true"
android:orientation="vertical"
@ -82,8 +85,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
app:srcCompat="@drawable/ic_directions_black_24dp"
android:tint="?attr/bookmarkButtonColor"
android:duplicateParentState="true"/>
android:duplicateParentState="true"
app:tint="?attr/bookmarkButtonColor" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -102,6 +105,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:padding="@dimen/standard_gap">
@ -110,8 +114,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:duplicateParentState="true"
android:tint="?attr/bookmarkButtonColor"
app:srcCompat="@drawable/ic_overflow" />
app:srcCompat="@drawable/ic_overflow"
app:tint="?attr/bookmarkButtonColor" />
<TextView
android:id="@+id/iconOverflowText"

View file

@ -42,10 +42,10 @@
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:contentDescription="@string/exit_location_picker"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_arrow_back_white"/>
app:srcCompat="@drawable/ic_arrow_back_white"
app:tint="@color/white" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,17 +1,25 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/item_refresh"
android:title="Refresh"
app:showAsAction="ifRoom"
android:icon="@drawable/ic_refresh_24dp_nearby" />
<item android:id="@+id/list_sheet"
android:title="@string/list_sheet"
app:showAsAction="ifRoom|withText"
android:icon="@drawable/ic_list_white_24dp"
/>
<item android:id="@+id/list_item_gpx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:title="Save as GPX file" />
<item android:id="@+id/list_item_kml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:title="Save as KML file" />
</menu>

View file

@ -527,6 +527,7 @@
<string name="no_notification">ليس لديك أي إشعارات غير مقروءة</string>
<string name="no_read_notification">ليس لديك أي إشعاراتٍ غير مقروءة</string>
<string name="share_logs_using">مشاركة السجلات باستخدام</string>
<string name="check_your_email_inbox">تحقق من صندوق بريدك الإلكتروني</string>
<string name="menu_option_read">عرض المقروءة</string>
<string name="menu_option_unread">عرض غير المقروءة</string>
<string name="error_occurred_in_picking_images">حدث خطأ أثناء التقاط الصور</string>
@ -690,6 +691,7 @@
<string name="leaderboard_nearby">مجاور</string>
<string name="leaderboard_used">مستخدَم</string>
<string name="leaderboard_my_rank_button_text">ترتيبي</string>
<string name="map_attribution">&amp;#169; &lt;a href=\"https://www.openstreetmap.org/copyright\"&gt;خريطة الشارع المفتوحة&lt;/a&gt;</string>
<string name="limited_connection_enabled">وضع الاتصال المحدود مُمَكَّن!</string>
<string name="limited_connection_disabled">وضع الاتصال المحدود مُعطل. سيجري استئناف التحميلات المعلقة الآن.</string>
<string name="limited_connection_mode">وضع الاتصال المحدود</string>
@ -739,6 +741,7 @@
<string name="custom_selector_dismiss_limit_warning_button_text">رفض</string>
<string name="custom_selector_button_limit_text">الحد الأقصى: %1$d</string>
<string name="custom_selector_limit_error_desc">خطأ: تجاوز حد التحميل</string>
<string name="place_state_wlm">دبليو إل إم</string>
<string name="wlm_upload_info">سيتم إدخال هذه الصورة في مسابقة Wiki Loves Monuments</string>
<string name="display_monuments">عرض الآثار</string>
<string name="wlm_month_message">إنه شهر Wiki Loves Monuments!</string>
@ -788,19 +791,66 @@
<string name="image_selected">تم تحديد الصورة</string>
<string name="image_marked_as_not_for_upload">تم وضع علامة على الصورة على أنها ليست للتحميل</string>
<string name="menu_view_report">تقرير</string>
<string name="menu_view_set_white_background">تعيين الخلفية البيضاء</string>
<string name="menu_view_set_black_background">تعيين خلفية سوداء</string>
<string name="report_violation">تبليغ عن عنف</string>
<string name="report_user">أخطر عن هذا المستخدم</string>
<string name="report_content">الإبلاغ عن هذا المحتوى</string>
<string name="request_user_block">طلب منع هذا المستخدم</string>
<string name="welcome_to_full_screen_mode_text">مرحبًا بك في وضع التحديد بملء الشاشة</string>
<string name="full_screen_mode_zoom_info">استخدم إصبعين للتكبير والتصغير.</string>
<string name="full_screen_mode_features_info" fuzzy="true">مرر سريعًا وطويلًا لتنفيذ هذه الإجراءات:! 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="similar_coordinate_description_auto_set">الإحداثيات ليست إحداثيات دقيقة، لكن الشخص الذي قام بتحميل هذه الصورة يعتقد أنها قريبة بما فيه الكفاية.</string>
<string name="storage_permissions_denied">رُفض إذن التخزين</string>
<string name="unable_to_share_upload_item">تعذر مشاركة هذا العنصر</string>
<string name="permissions_are_required_for_functionality">الإذن مطلوب لهذه الوظيفة</string>
<string name="learn_how_to_write_a_useful_description">تعلم كيفية كتابة وصف مفيد</string>
<string name="learn_how_to_write_a_useful_caption">تعلم كيفية كتابة تعليق مفيد</string>
<string name="see_your_achievements">شاهد إنجازاتك</string>
<string name="edit_image">تعديل الصورة</string>
<string name="edit_location">تعديل الموقع</string>
<string name="location_updated">تم تحديث الموقع!</string>
<string name="remove_location">إزالة الموقع</string>
<string name="remove_location_warning_title">إزالة تحذير الموقع</string>
<string name="remove_location_warning_desc">يجعل تحديد الموقع الصور أكثر فائدة وسهولة في العثور عليها. هل ترغب حقًا في إزالة تحديد الموقع من هذه الصورة؟</string>
<string name="location_removed">تمت إزالة الموقع!</string>
<string name="send_thanks_to_author">اشكر المؤلف</string>
<string name="error_sending_thanks">حدث خطأ أثناء إرسال الشكر للمؤلف.</string>
<string name="invalid_login_message">لقد انتهت صلاحية تسجيل الدخول الخاص بك. يرجى تسجيل الدخول مرة أخرى.</string>
<string name="no_application_available_to_open_gpx_files">لا يوجد تطبيق متاح لفتح ملفات GPX</string>
<string name="file_saved_successfully">تم حفظ الملف بنجاح</string>
<string name="do_you_want_to_open_gpx_file">هل تريد فتح ملف GPX؟</string>
<string name="do_you_want_to_open_kml_file">هل تريد فتح ملف KML؟</string>
<string name="failed_to_save_kml_file">فشل في حفظ ملف KML.</string>
<string name="failed_to_save_gpx_file">فشل في حفظ ملف GPX.</string>
<string name="saving_kml_file">حفظ ملف KML</string>
<string name="saving_gpx_file">حفظ ملف GPX</string>
<plurals name="custom_picker_images_selected_title_appendix">
<item quantity="zero">لا صور تم اختيارها</item>
<item quantity="one">%d صورة تم اختيارها</item>
<item quantity="two">صورتان تم اختيارهما</item>
<item quantity="few">صور قليلة تم اختيارها</item>
<item quantity="many">صور كثيرة تم اختيارها</item>
<item quantity="other">%d صور تم اختيارها</item>
</plurals>
<string name="multiple_files_depiction">يرجى تذكر أن جميع الصور في التحميل المتعدد تحصل على نفس الفئات والأوصاف. إذا لم تتشارك الصور في الأوصاف والفئات، فيرجى إجراء عدة عمليات تحميل منفصلة.</string>
<string name="multiple_files_depiction_header">ملاحظة حول التحميلات المتعددة</string>
<string name="nearby_wikitalk">الإبلاغ عن مشكلة حول هذا العنصر إلى Wikidata</string>
<string name="please_enter_some_comments">الرجاء إدخال بعض التعليقات</string>
<string name="talk">نقاش</string>
<string name="write_something_about_the_item">اكتب شيئًا عن العنصر \'%1$s\'. سيكون مرئيًا للعامة.</string>
<string name="does_not_exist_anymore_no_picture_can_ever_be_taken_of_it">\'%1$s\' لم يعد موجودًا، ولا يمكن التقاط صورة له أبدًا.</string>
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\'%1$s\' موجود في مكان مختلف. يرجى تحديد المكان الصحيح أدناه، وإذا أمكن، اكتب خط العرض وخط الطول الصحيحين.</string>
<string name="other_problem_or_information_please_explain_below">مشكلة أو معلومات أخرى (يرجى التوضيح أدناه).</string>
<string name="feedback_destination_note">سيتم نشر تعليقاتك على صفحة الويكي التالية: &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\"&gt;Commons:Mobile app/Feedback&lt;/a&gt;</string>
<string name="are_you_sure_that_you_want_cancel_all_the_uploads">هل أنت متأكد أنك تريد إلغاء كافة التحميلات؟</string>
<string name="cancelling_all_the_uploads">إلغاء كافة التحميلات...</string>
<string name="uploads">المرفوعات</string>
<string name="pending">قيد الانتظار</string>
<string name="failed">فشل</string>
<string name="could_not_load_place_data">تعذر تحميل بيانات المكان</string>
<string name="red_pin">هذا المكان ليس له صورة بعد، اذهب والتقط واحدة!</string>
<string name="green_pin">هذا المكان لديه صورة بالفعل.</string>
<string name="grey_pin">الآن التحقق ما إذا كان هذا المكان لديه صورة.</string>
</resources>

View file

@ -2,10 +2,11 @@
<!-- Authors:
* Dağlı95
* Khan27
* Nemoralis
-->
<resources>
<string name="crash_dialog_title">Nasazlıq</string>
<string name="crash_dialog_text">Uups. Nəsə düzgün çalışmır!</string>
<string name="crash_dialog_comment_prompt">Nə etdiyinizi dəqiqləşdirib, bizə bildirin və sonra e-poçtla bizə göndərin. Bu problemi həll etməyə bizə kömək edin.</string>
<string name="crash_dialog_ok_toast">Təşəkkür!</string>
<string name="crash_dialog_comment_prompt">Nə etdiyinizi bizə deyin, sonra e-poçt vasitəsilə bizimlə paylaşın. Bu, bizə bunu düzəltməyə kömək edəcək!</string>
<string name="crash_dialog_ok_toast">Təşəkkürlər!</string>
</resources>

View file

@ -12,34 +12,60 @@
* Şeyx Şamil
-->
<resources>
<string name="commons_facebook">Commons Facebook səhifəsi</string>
<string name="commons_github">Commons Github Mənbə Kodu</string>
<string name="commons_logo">Commons Loqotipi</string>
<string name="commons_website">Commons Veb-saytı</string>
<string name="exit_location_picker">Məkan seçicidən çıxın</string>
<string name="submit">Göndər</string>
<string name="add_another_description">Başqa təsvir əlavə et</string>
<string name="add_new_contribution">Yeni töhfə</string>
<string name="add_contribution_from_camera">Kamera ilə töhfə ver</string>
<string name="add_contribution_from_photos">Fotolar ilə töhfə ver</string>
<string name="add_contribution_from_contributions_gallery">Əvvəlki töhfələr qalereyasından töhfə əlavə et</string>
<string name="show_captions">Başlıqlar</string>
<string name="row_item_language_description">Dil təsviri</string>
<string name="row_item_caption">Başlıq</string>
<string name="show_captions_description">Təsvir</string>
<string name="nearby_row_image">Şəkil</string>
<string name="nearby_all">Hamısı</string>
<string name="nearby_filter_toggle">Aç/Bağla</string>
<string name="nearby_filter_search">Axtarış Görünüşü</string>
<string name="nearby_filter_state">Məkanın Vəziyyəti</string>
<string name="appwidget_img">Günün Şəkli</string>
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">%1$d fayl yüklənir</item>
<item quantity="other">%1$d fayllar yüklənir</item>
</plurals>
<plurals name="contributions_subtitle">
<item quantity="one">(%1$d)</item>
<item quantity="other">(%1$d)</item>
</plurals>
<string name="starting_uploads">Yükləmələrə Başlanılır</string>
<string name="preference_category_general">Ümumi</string>
<string name="preference_category_privacy">Məxfilik</string>
<string name="app_name">Vikimedia Commons</string>
<string name="app_name">Vikianbar</string>
<string name="menu_settings">Tənzimləmələr</string>
<string name="username">Ləqəb</string>
<string name="username">İstifadəçi adı</string>
<string name="password">Parol</string>
<string name="login">Daxil ol</string>
<string name="signup">Qeydiyyatdan keç</string>
<string name="logging_in_title">Giriş edilir</string>
<string name="logging_in_message">Lütfən gözləyin…</string>
<string name="logging_in_message">Zəhmət olmasa, gözləyin…</string>
<string name="login_success" fuzzy="true">Daxil oldunuz!</string>
<string name="login_failed">Giriş baş tutmadı!</string>
<string name="upload_failed">Fayl tapılmadı. Xahiş edirik başqa bir fayl üzərində cəhd edin.</string>
<string name="authentication_failed" fuzzy="true">Doğrulama alınmadı, xahiş edirəm yenidən daxil olun</string>
<string name="uploading_started">Yükləmə başladı!</string>
<string name="upload_completed_notification_title">%1$s yükləndi!</string>
<string name="upload_completed_notification_text">Yüklədiyini izlə</string>
<string name="upload_completed_notification_text">Yükləmənizə baxmaq üçün toxunun</string>
<string name="upload_progress_notification_title_start" fuzzy="true">%1$s yüklənməsi başlanır</string>
<string name="upload_progress_notification_title_in_progress">%1$s yüklənir</string>
<string name="upload_progress_notification_title_finishing">%1$s yüklənməsi başa çatdı</string>
<string name="upload_failed_notification_title" fuzzy="true">%1$s faylının yüklənməsi alınmadı</string>
<string name="upload_failed_notification_subtitle">Baxmaq üçün toxunun</string>
<string name="title_activity_contributions">Yükləmələrim</string>
<string name="contribution_state_queued">Sırada</string>
<string name="upload_failed_notification_subtitle">Baxmaq üçün toxun</string>
<string name="title_activity_contributions">Son Yükləmələrim</string>
<string name="contribution_state_queued">Növbəyə alındı</string>
<string name="contribution_state_failed">Uğursuz</string>
<string name="contribution_state_in_progress">%1$d%% tamamlandı</string>
<string name="contribution_state_starting">Yüklənir</string>
@ -52,15 +78,15 @@
<string name="share_description_hint">ıqlama</string>
<string name="login_failed_network" fuzzy="true">Daxil olmaq olmur — şəbəkə xətası</string>
<string name="login_failed_throttled">Çox sayda uğursuz daxil olma. Xahiş edirik bir neçə dəqiqə sonra yenidən cəhd edin.</string>
<string name="login_failed_blocked">Bağışlayın, bu istifadəçi Commons-da bloklanmışdır.</string>
<string name="login_failed_2fa_needed">İki faktorlu giriş doğrulama kodunu verməlisiniz.</string>
<string name="login_failed_blocked">Bağışlayın, bu istifadəçi Vikianbardan bloklanıb</string>
<string name="login_failed_2fa_needed">Siz iki faktorlu autentifikasiya kodunuzu təqdim etməlisiniz.</string>
<string name="login_failed_generic" fuzzy="true">Daxil olma uğursuz oldu</string>
<string name="share_upload_button">Yüklə</string>
<string name="multiple_share_base_title">Bu dəsti adlandırın</string>
<string name="provider_modifications">Bildirişlər</string>
<string name="provider_modifications">Dəyişikliklər</string>
<string name="menu_upload_single">Yüklə</string>
<string name="categories_search_text_hint">Kateqoriyaları axtar</string>
<string name="menu_save_categories">Qeyd et</string>
<string name="categories_search_text_hint">Kateqoriyalarda axtar</string>
<string name="menu_save_categories">Yadda saxla</string>
<string name="refresh_button">Yenilə</string>
<string name="display_list_button">Siyahı</string>
<string name="contributions_subtitle_zero">(Hələ yükləmə yoxdur)</string>

View file

@ -307,4 +307,5 @@
<string name="please_wait">Моля, изчакайте...</string>
<string name="delete_helper_ask_spam_blurry">напълно размазано</string>
<string name="leaderboard_nearby">Наблизо</string>
<string name="read_help_link">Прочетете повече</string>
</resources>

View file

@ -481,6 +481,7 @@
<string name="no_notification">Du har ingen ulæste notifikationer</string>
<string name="no_read_notification">Du har ingen læste notifikationer</string>
<string name="share_logs_using">Del logs ved hjælp af</string>
<string name="check_your_email_inbox">Tjek din e-mail-indbakke</string>
<string name="menu_option_read">Vis læste</string>
<string name="menu_option_unread">Vis ulæste</string>
<string name="error_occurred_in_picking_images">Der opstod en fejl under udvælgelse af billeder</string>
@ -788,4 +789,7 @@
<string name="pending">Afventer</string>
<string name="failed">Mislykkedes</string>
<string name="could_not_load_place_data">Kunne ikke indlæse steddata</string>
<string name="red_pin">Dette sted har endnu ikke noget billede, så gå hen og tag et!</string>
<string name="green_pin">Dette sted har allerede et billede.</string>
<string name="grey_pin">Tjekker nu, om dette sted har et billede.</string>
</resources>

View file

@ -22,6 +22,7 @@
* JenyxGym
* KATRINE1992
* Koreller
* Mahabarata
* McDutchie
* Melissadeba95
* Metroitendo
@ -516,6 +517,7 @@
<string name="no_notification">Vous navez aucune notification non lue</string>
<string name="no_read_notification">Vous navez aucune notification lue</string>
<string name="share_logs_using">Partager les journaux en utilisant</string>
<string name="check_your_email_inbox">Vérifiez votre boîte de réception</string>
<string name="menu_option_read">Afficher les lus</string>
<string name="menu_option_unread">Afficher les non lus</string>
<string name="error_occurred_in_picking_images">Une erreur est survenue lors de la sélection des images</string>
@ -825,4 +827,7 @@
<string name="pending">En attente</string>
<string name="failed">Échec</string>
<string name="could_not_load_place_data">Les données du lieu n\'ont pas pu être chargées</string>
<string name="red_pin">Cet endroit n\'a pas encore de photo, allez en prendre une !</string>
<string name="green_pin">Cet endroit a déjà une photo.</string>
<string name="grey_pin">Je vérifie maintenant si cet endroit a une photo.</string>
</resources>

View file

@ -374,7 +374,7 @@
<string name="unable_to_display_nearest_place">A helymeghatározás nélkül nem használható ez a funkció.</string>
<string name="never_ask_again">Ne kérdezd meg többször</string>
<string name="display_location_permission_title">Helymeghatározási engedély</string>
<string name="achievements_fetch_failed" fuzzy="true">Valami hiba történt, nem sikerült az eredményeid betöltése</string>
<string name="achievements_fetch_failed">Valami hiba történt, nem sikerült az eredményeid betöltése</string>
<string name="display_campaigns">Kampányok megjelenítése</string>
<string name="display_campaigns_explanation">Folyamatban lévő kampányok megjelenítése</string>
<string name="in_app_camera_location_access_explanation">Engedélyezd az alkalmazás számára a helyszín lekérését, ha a kamera nem rögzíti azt! Egyes eszközök kamerái nem rögzítik a helyszínt. Közreműködésed hasznosabb, ha ilyen esetekben hagyod, hogy az alkalmazás lekérje és hozzárendelje a helyszínt. Ezt bármikor módosíthatod a Beállításokban</string>
@ -385,7 +385,7 @@
<string name="in_app_camera_location_permission_denied">Az alkalmazás helymeghatározási engedély hiányában nem rögzíti a helyszínt a felvételekkel együtt</string>
<string name="in_app_camera_location_unavailable">Az alkalmazás nem rögzít helyszínt a felvételekkel együtt, mivel a GPS ki van kapcsolva</string>
<string name="nearby_campaign_dismiss_message">Többé nem lesznek láthatók a kampányok. Ha akarod, visszakapcsolható a Beállításoknál.</string>
<string name="this_function_needs_network_connection" fuzzy="true">Ehhez a funkcióhoz hálózati kapcsolat szükséges, kérlek ellenőrizd az internetbeállításaidat.</string>
<string name="this_function_needs_network_connection">Ehhez a funkcióhoz hálózati kapcsolat szükséges. Kérlek ellenőrizd az internetbeállításaidat!</string>
<string name="error_processing_image">Hiba történt a kép feltöltése során. Próbáld meg újra!</string>
<string name="getting_edit_token">Szerkesztő token beszerzése</string>
<string name="check_category_adding_template">Kategóriaellenőrző sablon felhelyezése</string>
@ -454,9 +454,9 @@
<string name="delete_helper_show_deletion_message_if">Törlésre jelölve: %1$s.</string>
<string name="delete_helper_show_deletion_title_failed">Sikertelen</string>
<string name="delete_helper_show_deletion_message_else">Nem sikerült a törlés kérése.</string>
<string name="delete_helper_ask_spam_selfie" fuzzy="true">Egy szelfi</string>
<string name="delete_helper_ask_spam_blurry" fuzzy="true">Homályos</string>
<string name="delete_helper_ask_spam_nonsense" fuzzy="true">Nonszensz</string>
<string name="delete_helper_ask_spam_selfie">egy szelfi, amely egyetlen cikkben sem szerepel</string>
<string name="delete_helper_ask_spam_blurry">teljesen homályos</string>
<string name="delete_helper_ask_spam_nonsense">nonszensz, abszolút használhatatlan bármely cikkben is</string>
<string name="delete_helper_ask_reason_copyright_press_photo">Sajtófotó</string>
<string name="delete_helper_ask_reason_copyright_internet_photo">Random fénykép az Internetről</string>
<string name="delete_helper_ask_reason_copyright_logo">Logó</string>
@ -472,7 +472,7 @@
<string name="place_state_needs_photo">Fénykép szükséges</string>
<string name="place_type">Hely típusa:</string>
<string name="nearby_search_hint">Híd, múzeum, szálloda, stb.</string>
<string name="you_must_reset_your_passsword" fuzzy="true">A belépés nem sikerült, kérj új jelszót.</string>
<string name="you_must_reset_your_passsword">A belépés nem sikerült. Kérj új jelszót!</string>
<string name="setting_wallpaper_dialog_title">Beállítás háttérképnek</string>
<string name="setting_wallpaper_dialog_message">Beállítás háttérképnek. Kérem várjon...</string>
<string name="theme_default_name">Rendszerbeállítás követése</string>

View file

@ -760,4 +760,7 @@
<item quantity="one">%d immagine selezionata</item>
<item quantity="other">%d immagini selezionate</item>
</plurals>
<string name="red_pin">Questo posto non ha ancora una foto, scattane una!</string>
<string name="green_pin">Questo posto ha già una foto.</string>
<string name="grey_pin">Ora controlliamo se questo posto ha una foto.</string>
</resources>

View file

@ -505,6 +505,7 @@
<string name="no_notification">אין לך התראות שלא נקראו</string>
<string name="no_read_notification">אין לך התראות שנקראו</string>
<string name="share_logs_using">שיתוף יומנים בעזרת</string>
<string name="check_your_email_inbox">לבדוק את תיבת הדוא״ל הנכנס שלך</string>
<string name="menu_option_read">הצגת התראות שנקראו</string>
<string name="menu_option_unread">הצגת התראות שלא נקראו</string>
<string name="error_occurred_in_picking_images">אירעה שגיאה בעת בחירת תמונות</string>
@ -820,4 +821,7 @@
<string name="pending">ממתינות</string>
<string name="failed">נכשלו</string>
<string name="could_not_load_place_data">לא היה אפשר לטעון את נתוני המקום</string>
<string name="red_pin">אין עדיין תמונה למקום הזה, אפשר פשוט לצלם אחת!</string>
<string name="green_pin">למקום הזה כבר יש תמונה.</string>
<string name="grey_pin">עכשיו מתבצעת בדיקה האם למקום הזה יש תמונה.</string>
</resources>

View file

@ -462,6 +462,7 @@
<string name="no_notification">未読の通知はありません</string>
<string name="no_read_notification">既読のお知らせはありません</string>
<string name="share_logs_using">ログの共有に使うアプリ</string>
<string name="check_your_email_inbox">メールをご確認ください</string>
<string name="menu_option_read">既読を表示</string>
<string name="menu_option_unread">未読を見る</string>
<string name="error_occurred_in_picking_images">画像の選択中にエラーが発生しました</string>
@ -679,7 +680,6 @@
<string name="error_sending_thanks">作者への感謝の送信エラー。</string>
<string name="invalid_login_message">ログインが期限切れになりました。もう一度ログインしてください。</string>
<string name="no_application_available_to_open_gpx_files">GPXファイルを開くことができるアプリケーションがありません</string>
<string name="check_your_email_inbox">メールをご確認ください</string>
<plurals name="custom_picker_images_selected_title_appendix">
<item quantity="other">%d件の画像が選択されました</item>
</plurals>

View file

@ -284,6 +284,7 @@
<string name="wikicode_copied">위키텍스트를 클립보드에 복사했습니다</string>
<string name="nearby_location_not_available">주변이 제대로 작동되지 않을 수 있습니다. 위치를 사용할 수 없습니다.</string>
<string name="location_permission_rationale_nearby">주변 장소의 목록을 표시하기 위한 권한이 필요합니다.</string>
<string name="location_permission_rationale_explore">주변 장소의 이미지 목록을 표시하기 위한 권한이 필요합니다</string>
<string name="nearby_directions">방향</string>
<string name="nearby_wikidata">위키데이터</string>
<string name="nearby_wikipedia">위키백과</string>
@ -439,6 +440,7 @@
<string name="nominate_for_deletion_done">완료</string>
<string name="send_thank_success_title">감사 표현 보내기: 성공</string>
<string name="send_thank_failure_title">감사 표현 보내기: 실패</string>
<string name="review_copyright">이것이 저작권 규정을 준수하고 있습니까?</string>
<string name="review_category">알맞게 분류됐습니까?</string>
<string name="review_thanks">기여자에게 감사를 표하시겠습니까?</string>
<string name="review_no_category">앗, 분류가 달리지 않은 것 같습니다!</string>
@ -453,6 +455,7 @@
<string name="no_image_uploaded">이미지가 올려지지 않음</string>
<string name="no_notification">읽지 않은 알림이 없습니다</string>
<string name="no_read_notification">읽은 알림이 없습니다</string>
<string name="check_your_email_inbox">이메일의 받은 편지함을 확인하세요</string>
<string name="menu_option_read">읽은 항목 보기</string>
<string name="menu_option_unread">읽지 않은 항목 보기</string>
<string name="error_occurred_in_picking_images">이미지 선택 도중 오류가 발생했습니다</string>
@ -499,6 +502,7 @@
<string name="coordinates_edit_helper_show_edit_title_success">성공</string>
<string name="description_edit_helper_show_edit_message">설명이 추가되었습니다.</string>
<string name="caption_edit_helper_show_edit_message">캡션이 추가되었습니다.</string>
<string name="coordinates_edit_helper_edit_message_else">좌표를 추가하지 못했습니다.</string>
<string name="description_edit_helper_edit_message_else">설명을 추가하지 못했습니다.</string>
<string name="caption_edit_helper_edit_message_else">캡션을 추가하지 못했습니다.</string>
<string name="coordinates_picking_unsuccessful">이미지 좌표가 업데이트되지 않았습니다</string>
@ -549,6 +553,7 @@
<string name="pause">일시 정지</string>
<string name="resume">계속하기</string>
<string name="paused">일시 중단됨</string>
<string name="more">더 보기</string>
<string name="bookmarks">책갈피</string>
<string name="leaderboard_tab_title">리더보드</string>
<string name="rank_prefix">순위:</string>
@ -644,6 +649,9 @@
<string name="welcome_to_full_screen_mode_text">전체 화면 선택 모드에 오신 것을 환영합니다</string>
<string name="full_screen_mode_zoom_info">두 손가락으로 확대 / 축소하세요.</string>
<string name="full_screen_mode_features_info">다음 방향으로 길고 재빠르게 넘겨보세요. \n- 왼쪽/오른쪽: 이전/다음으로 이동 \n- 위쪽: 선택\n- 아래쪽: 비업로드용으로 표시</string>
<string name="storage_permissions_denied">스토리지 접근이 거부됨</string>
<string name="unable_to_share_upload_item">이 항목을 공유할 수 없습니다</string>
<string name="permissions_are_required_for_functionality">기능에 대한 권한이 필요합니다</string>
<string name="learn_how_to_write_a_useful_description">유용한 설명을 추가하는 법 알아보기</string>
<string name="learn_how_to_write_a_useful_caption">유용한 캡션을 추가하는 법 알아보기</string>
<string name="see_your_achievements">업적 보기</string>
@ -657,6 +665,7 @@
<string name="send_thanks_to_author">작성자에게 감사 표시하기</string>
<string name="error_sending_thanks">작성자에게 감사를 표하던 도중에 오류가 발생하였습니다.</string>
<string name="invalid_login_message">로그인 세션 만료. 다시 로그인해 주십시오.</string>
<string name="no_application_available_to_open_gpx_files">GPX 파일을 열 수 있는 응용 프로그램이 없습니다</string>
<string name="file_saved_successfully">파일이 성공적으로 저장되었습니다</string>
<string name="do_you_want_to_open_gpx_file">GPX 파일을 여시겠습니까?</string>
<string name="do_you_want_to_open_kml_file">KML 파일을 여시겠습니까?</string>
@ -664,8 +673,20 @@
<string name="failed_to_save_gpx_file">GPX 파일을 저장하지 못했습니다.</string>
<string name="saving_kml_file">KML 파일을 저장 중</string>
<string name="saving_gpx_file">GPX 파일을 저장 중</string>
<plurals name="custom_picker_images_selected_title_appendix">
<item quantity="other">%d개 이미지 선택됨</item>
</plurals>
<string name="multiple_files_depiction_header">다중 업로드에 대한 참고사항</string>
<string name="nearby_wikitalk">이 항목에 관한 문제를 위키데이터에 보고하기</string>
<string name="please_enter_some_comments">의견을 입력해 주십시오</string>
<string name="talk">토론</string>
<string name="other_problem_or_information_please_explain_below">기타 문제 또는 정보 (아래에 설명해 주십시오)</string>
<string name="cancelling_all_the_uploads">모든 업로드를 취소하는 중...</string>
<string name="uploads">업로드</string>
<string name="pending">보류 중</string>
<string name="failed">실패</string>
<string name="could_not_load_place_data">장소 데이터를 불러오지 못했습니다</string>
<string name="red_pin">이 장소에 아직 사진이 없습니다. 사진을 찍어보세요!</string>
<string name="green_pin">이 장소에 이미 사진이 있습니다.</string>
<string name="grey_pin">지금 이 장소에 사진이 있는지 확인 중입니다.</string>
</resources>

View file

@ -227,7 +227,7 @@
<string name="cancel">Ызына ал</string>
<string name="navigation_drawer_open">Ач</string>
<string name="navigation_drawer_close">Джаб</string>
<string name="navigation_item_home">Баш бет</string>
<string name="navigation_item_home">Тамал бет</string>
<string name="navigation_item_upload">Джюкле</string>
<string name="navigation_item_nearby">Джуўукъда</string>
<string name="navigation_item_about">Юсюнден</string>
@ -473,6 +473,7 @@
<string name="no_notification">Окъулмагъана хапарландырыуугъуз джокъду</string>
<string name="no_read_notification">Окъулмагъан хапарландырыуугъуз барды</string>
<string name="share_logs_using">Логларыгъызны хайырланыб юлюшлегиз</string>
<string name="check_your_email_inbox">Электрон почтагъызны тинтигиз</string>
<string name="menu_option_read">Окъулгъанны кёргюз</string>
<string name="menu_option_unread">Окъулмагъанланы кёргюз</string>
<string name="error_occurred_in_picking_images">Суратла сайланнган заманда халат болду.</string>
@ -686,7 +687,7 @@
<string name="read_phone_state_permission_message">Джууукъдагъы картала тюз ишлер ючюн ТЕЛЕФОННУ БОЛУМУн окъургъа амал болургъа кереклиди</string>
<string name="contributions_of_user">Хайырланыучуну къошумлары: %s</string>
<string name="achievements_of_user">Хайырланыучуну джетишимлери: %s</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_categories">Категорияланы тюзет</string>
<string name="advanced_options">Кенгленнген Сайлаула</string>
@ -774,4 +775,13 @@
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\'%1$s\' - башха джерди. Тилейбиз, тюз джерни энишгерекде белгилегиз, эмда мадар бар эсе, тюз кенглик бла узунлукъну джазыгъыз.</string>
<string name="other_problem_or_information_please_explain_below">Башха проблема неда информация (тилейбиз, энишгерекде ангылатыгъыз).</string>
<string name="feedback_destination_note">Сизни кери оюмугъуз тюбюндеги вики бетге джиберилликди: &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\"&gt;Commons:Mobile app/Feedback&lt;/a&gt;</string>
<string name="are_you_sure_that_you_want_cancel_all_the_uploads">Бютеу джюклеулени тохтатыргъа излегенигизге ишексизмисиз?</string>
<string name="cancelling_all_the_uploads">Бютеу джюклеулени тохтатыу...</string>
<string name="uploads">Джюклеуле</string>
<string name="pending">Сакълауда</string>
<string name="failed">Джетишимсиз</string>
<string name="could_not_load_place_data">Джерни юсюнден билгилени джюклеялмады</string>
<string name="red_pin">Бу джерни сураты джокъду, хайда бирин эт!</string>
<string name="green_pin">Бу джерни алайсыз да сураты барды.</string>
<string name="grey_pin">Бу джерни сураты болуб-болмагъанын тинте турама.</string>
</resources>

View file

@ -72,8 +72,9 @@
<string name="upload_completed_notification_text">Dréckt fir de Fichier ze gesinn deen Dir eropgelueden hutt</string>
<string name="upload_progress_notification_title_start">Fichier eroplueden: %s</string>
<string name="upload_progress_notification_title_in_progress">%1$s gëtt eropgelueden</string>
<string name="upload_progress_notification_title_finishing">Eroplueden vu(n) %1$s ofschléissen</string>
<string name="upload_progress_notification_title_finishing">Eropluede vu(n) %1$s ofschléissen</string>
<string name="upload_failed_notification_title">%1$s konnt net eropgeluede ginn</string>
<string name="upload_paused_notification_title">Eropluede vu(n) %1$s pauséiert</string>
<string name="upload_failed_notification_subtitle">Dréckt fir nach eng Kéier ze probéieren</string>
<string name="upload_paused_notification_subtitle">Tippe fir ze kucken</string>
<string name="title_activity_contributions">Meng rezent eropgeluede Fichieren</string>
@ -86,6 +87,7 @@
<string name="menu_nearby">Nobäi</string>
<string name="provider_contributions">Meng eropgeluede Fichieren</string>
<string name="menu_share">Deelen</string>
<string name="menu_view_file_page">Fichierssäit weisen</string>
<string name="share_title_hint">Beschrëftung (obligatoresch)</string>
<string name="add_caption_toast">Gitt wgl. eng Beschrëftung fir dëse Fichier un</string>
<string name="share_description_hint">Beschreiwung</string>
@ -93,6 +95,7 @@
<string name="login_failed_network">Aloggen huet net funktionéiert Problemer mam Reseau</string>
<string name="login_failed_throttled">Ze dacks ouni Succès probéiert. Probéiert wgl. an e puer Minutten nach eng Kéier.</string>
<string name="login_failed_blocked">Pardon, dëse Benotzer ass op Commons gespaart</string>
<string name="login_failed_2fa_needed">Dir musst de Code vun Ärer Zwee-Facteur-Authentifizéierung uginn.</string>
<string name="login_failed_generic">Aloggen huet net funktionéiert</string>
<string name="share_upload_button">Eroplueden</string>
<string name="multiple_share_base_title">Gitt dëser Biller een Numm</string>
@ -498,11 +501,20 @@
<string name="report_content">Dësen Inhalt mellen</string>
<string name="request_user_block">Ufroe fir dëse Benotzer ze spären</string>
<string name="learn_how_to_write_a_useful_caption">Léiert, wéi een nëtzlech Beschrëftunge schreift</string>
<string name="edit_image">Bild änneren</string>
<string name="edit_location">Plaz änneren</string>
<string name="location_updated">Plaz aktualiséiert!</string>
<string name="remove_location">Plaz ewechhuelen</string>
<string name="remove_location_warning_title">Plaz-Ewechhuele-Warnung</string>
<string name="remove_location_warning_desc">Duerch Plaze sinn d\'Biller méi nëtzlech a besser sichbar. Wëllt Dir wierklech d\'Plaz vun dësem Bild ewechhuelen?</string>
<string name="location_removed">Plat ewechgeholl!</string>
<string name="send_thanks_to_author">Dem Auteur Merci soen</string>
<string name="error_sending_thanks">Feeler beim Schécke vun engem Merci un den Auteur.</string>
<string name="file_saved_successfully">Fichier erfollegräich gespäichert</string>
<string name="do_you_want_to_open_gpx_file">Wëllt Dir de GPW-Fichier opmaachen?</string>
<string name="do_you_want_to_open_kml_file">Wëllt Dir de KML-Fichier opmaachen?</string>
<string name="failed_to_save_kml_file">De KML-Fichier konnt net gespäichert ginn.</string>
<string name="failed_to_save_gpx_file">De GPX-Fichier konnt net gespäichert ginn.</string>
<string name="saving_kml_file">KML-Fichier späicheren</string>
<string name="saving_gpx_file">GPX-Fichier späicheren</string>
<plurals name="custom_picker_images_selected_title_appendix">

View file

@ -28,6 +28,7 @@
<string name="show_captions_description">Aprašymas</string>
<string name="nearby_row_image">Paveikslėlis</string>
<string name="nearby_all">Visi</string>
<string name="nearby_filter_toggle">Perjungti aukštyn</string>
<string name="nearby_filter_search">Paieškos rodinys</string>
<string name="appwidget_img">Dienos nuotrauka</string>
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
@ -57,6 +58,7 @@
<string name="app_name">Vikiteka</string>
<string name="menu_settings">Nustatymai</string>
<string name="intent_share_upload_label">Įkelti į Vikiteką</string>
<string name="upload_in_progress">Vyksta įkėlimas</string>
<string name="username">Naudotojo vardas</string>
<string name="password">Slaptažodis</string>
<string name="login_credential">Prisijunkite prie savo Commons Beta paskyros</string>
@ -67,10 +69,13 @@
<string name="logging_in_message">Prašome palaukti…</string>
<string name="updating_caption_title">Antraštės ir aprašymai atnaujinami</string>
<string name="updating_caption_message">Prašome palaukti...</string>
<string name="login_success" fuzzy="true">Sėkmingai prisijungėte!</string>
<string name="login_failed" fuzzy="true">Prisijungti nepavyko!</string>
<string name="login_success">Sėkmingai prisijungėte!</string>
<string name="login_failed">Prisijungti nepavyko!</string>
<string name="upload_failed">Failas nerastas. Prašome pabandyti kitą failą.</string>
<string name="authentication_failed" fuzzy="true">Autentifikavimas nepavyko, prašome prisijungti dar kartą</string>
<string name="retry_limit_reached">Pasiektas maksimalus pakartotinių bandymų limitas! Atšaukite įkėlimą ir bandykite dar kartą</string>
<string name="unrestricted_battery_mode">Išjungti akumuliatoriaus optimizavimą?</string>
<string name="suggest_unrestricted_mode">Daugiau nei 3 paveikslėlių įkėlimas veikia patikimiau, kai akumuliatoriaus optimizavimas išjungtas. Išjunkite Vikitekos programėlės akumuliatoriaus optimizavimą nustatymuose, kad įkėlimas būtų sklandus. \n\nGalimi akumuliatoriaus optimizavimo išjungimo veiksmai:\n\n1 veiksmas: bakstelėkite toliau esantį mygtuką „Nustatymai“.\n\n2 veiksmas: perjunkite iš „Neoptimizuota“ į „Visos programėlės“.\n\n3 veiksmas: ieškokite „Vikiteka“ arba „fr.free.nrw.commons“.\n\n4 veiksmas: spustelėkite jį ir pasirinkite „Neoptimizuoti“.\n\n5 veiksmas: paspauskite „Atlikta“.</string>
<string name="authentication_failed">Autentifikavimas nepavyko. Prašome prisijungti dar kartą.</string>
<string name="uploading_started">Įkėlimas prasidėjo!</string>
<string name="uploading_queued">Įkėlimas eilėje (įgalintas riboto ryšio režimas)</string>
<string name="upload_completed_notification_title">%1$s įkelta!</string>
@ -97,11 +102,11 @@
<string name="add_caption_toast">Pateikite šio failo antraštę</string>
<string name="share_description_hint">Aprašymas</string>
<string name="share_caption_hint">Antraštė</string>
<string name="login_failed_network" fuzzy="true">Negalima prisijungti - tinklo klaida</string>
<string name="login_failed_network">Negalima prisijungti - tinklo klaida</string>
<string name="login_failed_throttled">Per daug nesėkmingų bandymų. Pabandykite dar kartą po keleto minučių.</string>
<string name="login_failed_blocked">Atsiprašome, šis vartotojas buvo užblokuotas Commons</string>
<string name="login_failed_2fa_needed">Turite pateikti savo dviejų žingsnių patvirtinimo kodą.</string>
<string name="login_failed_generic" fuzzy="true">Prisijungti nepavyko</string>
<string name="login_failed_generic">Prisijungti nepavyko</string>
<string name="share_upload_button">Įkelti</string>
<string name="multiple_share_base_title">Pavadinkite šį rinkinį</string>
<string name="provider_modifications">Pakeitimai</string>
@ -123,7 +128,7 @@
<string name="title_activity_category_details">Kategorija</string>
<string name="menu_about">Apie</string>
<string name="about_license">Vikitekos programėlė yra atviro kodo programėlė, kurią sukūrė ir prižiūri Vikitekos bendruomenės dotacijų gavėjai ir savanoriai. „Wikimedia Foundation“ nedalyvauja kuriant, plėtojant ar prižiūrint programėlę.</string>
<string name="about_improve" fuzzy="true">Sukurkite naują &lt;a href=\"%1$s\"&gt;GitHub pranešimą&lt;/a&gt;, siekiant pranešti apie klaidas ir pateikti siūlymus.</string>
<string name="about_improve">Sukurkite naują &lt;a href=\"%1$s\"&gt;GitHub pranešimą&lt;/a&gt;, siekiant pranešti apie klaidas ir pateikti siūlymus.</string>
<string name="about_privacy_policy">Privatumo politika</string>
<string name="about_credits">Kūrėjai</string>
<string name="title_activity_about">Apie</string>
@ -178,6 +183,8 @@
<string name="read_storage_permission_rationale">Reikalinga teisė: Skaityti išorinę talpyklą. Programėle be to negali prieiti prie jūsų galerijos.</string>
<string name="write_storage_permission_rationale">Reikalingas leidimas: rašyti į išorinę saugyklą. Programėlė be to negali pasiekti jūsų fotoaparato/galerijos.</string>
<string name="location_permission_title">Prašoma vietovės leidimo</string>
<string name="in_app_camera_location_permission_title">Įrašyti vietovę nuotraukoms, kurios fotografuotos programėlėje</string>
<string name="in_app_camera_location_switch_pref_summary">Įjunkite šią funkciją, kad įrašytumėte vietovę nuotraukoms, jei jūsų įrenginio kamera to nepadaro</string>
<string name="ok">Gerai</string>
<string name="warning">Įspėjimas</string>
<string name="duplicate_file_name">Rastas pasikartojantis failo pavadinimas</string>
@ -198,6 +205,7 @@
<string name="become_a_tester_description">Prisijunkite prie beta kanalo Google Play ir gaukite išankstinę prieigą prie naujų funkcijų bei klaidų pataisymų</string>
<string name="_2fa_code">2FA kodas</string>
<string name="logout_verification">Ar tikrai norite atsijungti?</string>
<string name="mediaimage_failed">Medijos paveikslėlis nepavyko</string>
<string name="no_subcategory_found">Subkategorijų nerasta</string>
<string name="welcome_image_mount_zao">Zao kalnas</string>
<string name="welcome_image_llamas">Lamos</string>
@ -214,6 +222,7 @@
<string name="navigation_item_about">Apie</string>
<string name="navigation_item_settings">Nustatymai</string>
<string name="navigation_item_feedback">Atsiliepimai</string>
<string name="navigation_item_feedback_github">Atsiliepimai per GitHub</string>
<string name="navigation_item_logout">Atsijungti</string>
<string name="navigation_item_info">Pamoka</string>
<string name="navigation_item_notification">Pranešimai</string>
@ -247,13 +256,15 @@
<string name="nominated_see_more">Žiūrėkite tinklapį dėl daugiau informacijos</string>
<string name="skip_login">Praleisti</string>
<string name="navigation_item_login">Prisijungti</string>
<string name="skip_login_title" fuzzy="true">Ar tikrai norite praleisti prisijungimą?</string>
<string name="skip_login_message" fuzzy="true">Norėdami ateityje įkelti nuotraukas, turėsite prisijungti.</string>
<string name="skip_login_title">Ar tikrai norite praleisti prisijungimą?</string>
<string name="skip_login_message">Norėdami ateityje įkelti nuotraukas, turėsite prisijungti.</string>
<string name="login_alert_message">Jei norite naudotis šia funkcija, prisijunkite</string>
<string name="copy_wikicode">Nukopijuokite vikitekstą į mainų sritį</string>
<string name="wikicode_copied">Vikitekstas buvo nukopijuotas į mainų sritį</string>
<string name="nearby_location_not_available">Netoliese gali tinkamai neveikti, vieta nepasiekiama.</string>
<string name="upload_location_access_denied">Prieiga prie vietos uždrausta. Norėdami naudotis šia funkcija, nustatykite savo vietą rankiniu būdu.</string>
<string name="location_permission_rationale_nearby">Norint rodyti netoliese esančių vietų sąrašą, reikalingas leidimas</string>
<string name="location_permission_rationale_explore">Norint rodyti netoliese esančių paveikslėlių sąrašą, reikalingas leidimas</string>
<string name="nearby_directions">Nurodymai</string>
<string name="nearby_wikidata">Vikiduomenys</string>
<string name="nearby_wikipedia">Vikipedija</string>
@ -313,14 +324,16 @@
<string name="construction_event_answer">Nuotraukos, kuriose pavaizduotos technologijos ar kultūra, yra labai laukiamos Vikitekoje.</string>
<string name="congratulatory_message_quiz">Surinkote %1$s teisingų atsakymų. Sveikiname!</string>
<string name="warning_for_no_answer">Norėdami atsakyti į klausimą, pasirinkite vieną iš dviejų variantų</string>
<string name="user_not_logged_in" fuzzy="true">Prisijungimo sesija baigėsi, prisijunkite dar kartą.</string>
<string name="user_not_logged_in">Prisijungimo sesija baigėsi, prašome prisijungti dar kartą.</string>
<string name="quiz_result_share_message">Pasidalinkite savo apkalusa su draugais!</string>
<string name="continue_message">Tęsti</string>
<string name="correct">Teisingas atsakymas</string>
<string name="wrong">Atsakymas neteisingas</string>
<string name="quiz_screenshot_question">Ar šią ekrano kopiją galima įkelti?</string>
<string name="share_app_title">Dalintis programėle</string>
<string name="error_fetching_nearby_places" fuzzy="true">Klaida gaunant netoliese esančias vietas.</string>
<string name="rotate">Pasukti</string>
<string name="error_fetching_nearby_places">Nepavyko įkelti netoliese esančių vietų</string>
<string name="no_pictures_in_this_area">Šioje vietovėje nuotraukų nėra</string>
<string name="no_nearby_places_around">Nėra šalia esančių vietų</string>
<string name="error_fetching_nearby_monuments">Gaunant netoliese esančius paminklus įvyko klaida.</string>
<string name="no_recent_searches">Nėra naujausių paieškų</string>
@ -338,6 +351,7 @@
<string name="statistics_wikidata_edits">Vaizdai per „Netoliese esančios vietos“</string>
<string name="level">Lygis</string>
<string name="images_uploaded">Vaizdai įkelti</string>
<string name="image_reverts">Paveikslėliai negrąžinti</string>
<string name="images_used_by_wiki">Naudoti vaizdai</string>
<string name="achievements_share_message">Pasidalinkite savo pasiekimais su draugais!</string>
<string name="achievements_info_message">Jūsų lygis kyla, kai atitinkate šiuos reikalavimus. Skiltyje „statistika“ esantys elementai neįskaičiuojami į jūsų lygį.</string>
@ -394,12 +408,22 @@
<string name="never_ask_again">Niekada daugiau to neklausti</string>
<string name="display_location_permission_title">Paprašyti vietos leidimo</string>
<string name="display_location_permission_explanation">Jei reikia, kad būtų galima naudoti netoliese esančio pranešimų kortelės peržiūros funkciją, paprašykite leidimo nustatyti vietą.</string>
<string name="achievements_fetch_failed" fuzzy="true">Kažkas ne taip. Nepavyko gauti jūsų pasiekimų</string>
<string name="achievements_fetch_failed">Kažkas ne taip, mums nepavyko gauti pasiekimų</string>
<string name="achievements_fetch_failed_ultimate_achievement">Prisidėjote tiek daug, kad mūsų pasiekimų skaičiavimo sistema negali susidoroti. Tai yra didžiausias pasiekimas.</string>
<string name="ends_on">Baigiasi:</string>
<string name="display_campaigns_explanation">Peržiūrėkite vykstančias kampanijas</string>
<string name="in_app_camera_location_access_explanation">Leiskite programėlei nuskaityti vietą, jei fotoaparatas jos neįrašo. Kai kurių įrenginių kameros neįrašo vietos. Tokiais atvejais leidus programai gauti ir pridėti vietą, jūsų indėlis bus naudingesnis. Tai galite bet kada pakeisti nustatymuose</string>
<string name="option_allow">Leisti</string>
<string name="option_dismiss">Paslėpti</string>
<string name="in_app_camera_needs_location">Nustatymuose įjunkite prieigą prie vietos ir bandykite dar kartą. \n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio.</string>
<string name="in_app_camera_location_permission_rationale">Programėlėje esančiam fotoaparatui reikalingas vietos leidimas, kad jis būtų pridėtas prie vaizdų, jei EXIF nėra vietos. Leiskite programėlei pasiekti jūsų buvimo vietą ir bandykite dar kartą.\n\nPastaba: įkėlimas gali neturėti vietos, jei programėlė negali per trumpą laiką nuskaityti vietos iš įrenginio.</string>
<string name="in_app_camera_location_permission_denied">Programėlė neįrašys vietos kartu su kadrais, nes neturi vietos leidimo</string>
<string name="in_app_camera_location_unavailable">Programa neįrašys vietos kartu su kadrais, nes GPS išjungtas</string>
<string name="open_document_photo_picker_title">Naudokite dokumentais pagrįstą nuotraukų rinkiklį</string>
<string name="open_document_photo_picker_explanation">Naujasis „Android“ nuotraukų rinkiklis gali prarasti vietos informaciją. Įjunkite, jei atrodo, kad jį naudojate.</string>
<string name="location_loss_warning">Išjungus tai gali suaktyvinti naująjį „Android“ nuotraukų rinkiklį. Dėl to kyla pavojus prarasti vietos informaciją.\n\nNorėdami gauti daugiau informacijos, bakstelėkite „Skaityti daugiau“.</string>
<string name="nearby_campaign_dismiss_message">Kampanijų nebematysite. Tačiau, jei norite, galite iš naujo įjungti šį pranešimą nustatymuose.</string>
<string name="this_function_needs_network_connection" fuzzy="true">Šiai funkcijai reikalingas tinklo ryšys, patikrinkite ryšio nustatymus.</string>
<string name="this_function_needs_network_connection">Šiai funkcijai reikalingas tinklo ryšys. Prašome patikrinti savo ryšio nustatymus.</string>
<string name="error_processing_image">Apdorojant vaizdą įvyko klaida. Pabandykite dar kartą!</string>
<string name="getting_edit_token">Gaunamas redagavimo prieigos raktas</string>
<string name="check_category_adding_template">Kategorijos tikrinimo šablonas pridedamas</string>
@ -418,22 +442,26 @@
<string name="send_thank_toast">Siunčiama padėka už %1$s</string>
<string name="review_copyright">Ar tai atitinka autorines teises?</string>
<string name="review_category">Ar tai teisingai priskirta kategorijoms?</string>
<string name="review_spam">Ar tai taikytina?</string>
<string name="review_thanks">Ar norėtumėte padėkoti prisidėjusiam?</string>
<string name="review_spam_explanation">Spustelėkite NE, kad pasiūlytumėte šį vaizdą ištrinti, jei jis visai nenaudingas.</string>
<string name="review_copyright_explanation">Logotipai, ekrano kopijos, filmų plakatai dažnai pažeidžia autorines teises. Spustelėkite NE, jei norite pasiūlyti šį vaizdą ištrinti</string>
<string name="review_thanks_explanation">%1$s bus padrąsintas jūsų dėkingumu</string>
<string name="review_no_category">Oi, tai net nėra priskirta kategorijai!</string>
<string name="review_category_explanation">Šis vaizdas priklauso %1$s kategorijoms.</string>
<string name="review_spam_report_question">Tai nėra taikytina, nes</string>
<string name="review_c_violation_report_question">Tai yra autorių teisių pažeidimas, nes</string>
<string name="review_thanks_yes_button_text">Kitas vaizdas</string>
<string name="review_thanks_no_button_text">Taip, kodėl gi ne</string>
<string name="skip_image_explanation">Spustelėję šį mygtuką pamatysite kitą neseniai įkeltą vaizdą iš Vikitekos</string>
<string name="review_image_explanation">Galite peržiūrėti vaizdus, kad pagerintumėte Vikitekos kokybę.\nTrys peržiūros parametrai yra:\n\n- Ar šis vaizdas tinkamas?\nKai paliesite Ne (nepatenka į sritį), jūs prie šio paveikslėlio pridedate ištrynimo nominacijos šabloną.\n\n- Ar šis vaizdas atitinka autorių teisių taisykles?\nKai paliesite Ne (neatitinka autorių teisių taisyklių), pridedate ištrynimo nominacijos šabloną prie šio paveikslėlio.\n\n- Ar šis vaizdas teisingai suskirstytas į kategorijas?\nKai paliesite Ne (neteisingai suskirstytas į kategorijas), prie šio paveikslėlio pridedate kategorizavimo užklausos šabloną.\n\nJei viskas yra gerai, prie paveikslėlio nepridedamas joks šablonas, ir jūs turite galimybę padėkoti bendraautoriui.</string>
<string name="no_image">Nenaudojami jokie vaizdai</string>
<string name="no_image_reverted">Jokie vaizdai negrąžinti</string>
<string name="no_image_uploaded">Neįkelta jokių vaizdų</string>
<string name="no_notification">Neturite neskaitytų pranešimų</string>
<string name="no_read_notification">Neturite perskaitytų pranešimų</string>
<string name="share_logs_using">Dalinkitės žurnalus naudodami</string>
<string name="check_your_email_inbox">Patikrinkite savo el. pašto dėžutę</string>
<string name="menu_option_read">Žiūrėti perskaitytus</string>
<string name="menu_option_unread">Žiūrėti neperskaitytus</string>
<string name="error_occurred_in_picking_images">Renkant vaizdus įvyko klaida</string>
@ -478,6 +506,7 @@
<string name="delete_helper_ask_reason_copyright_press_photo">Žiniasklaidos nuotrauka</string>
<string name="delete_helper_ask_reason_copyright_internet_photo">Atsitiktinė nuotrauka iš interneto</string>
<string name="delete_helper_ask_reason_copyright_logo">Logotipas</string>
<string name="delete_helper_ask_reason_copyright_no_freedom_of_panorama">Panoramos laisvės pažeidimas</string>
<string name="delete_helper_ask_alert_set_positive_button_reason">Nes</string>
<string name="category_edit_helper_make_edit_toast">Bandoma atnaujinti kategorijas.</string>
<string name="category_edit_helper_show_edit_title">Kategorijos atnaujinimas</string>
@ -498,7 +527,7 @@
<string name="coordinates_edit_helper_edit_message_else">Nepavyko pridėti koordinačių.</string>
<string name="description_edit_helper_edit_message_else">Nepavyko pridėti aprašymų.</string>
<string name="caption_edit_helper_edit_message_else">Nepavyko pridėti antraštę.</string>
<string name="coordinates_picking_unsuccessful" fuzzy="true">Nepavyko gauti koordinačių.</string>
<string name="coordinates_picking_unsuccessful">Paveikslėlio koordinatės neatnaujintos</string>
<string name="descriptions_picking_unsuccessful">Nepavyko gauti aprašymų.</string>
<string name="description_activity_title">Redaguokite aprašymus ir antraštes</string>
<string name="share_image_via">Dalintis vaizdu per</string>
@ -513,10 +542,11 @@
<string name="place_state_needs_photo">Reikia Nuotraukos</string>
<string name="place_type">Vietos tipas:</string>
<string name="nearby_search_hint">Tiltas, muziejus, viešbutis ir t.t.</string>
<string name="you_must_reset_your_passsword" fuzzy="true">Kažkas nepavyko prisijungiant, turite iš naujo nustatyti slaptažodį !!</string>
<string name="you_must_reset_your_passsword">Kažkas nepavyko prisijungiant. Turite iš naujo nustatyti slaptažodį!</string>
<string name="title_for_media">MEDIJA</string>
<string name="upload_nearby_place_found_title">Netoliese rasta vieta</string>
<string name="upload_nearby_place_found_description_singular" fuzzy="true">Ar tai vietos %1$s nuotrauka?</string>
<string name="upload_nearby_place_found_description_plural">Ar tai %1$s nuotraukos?</string>
<string name="upload_nearby_place_found_description_singular">Ar tai %1$s nuotrauka?</string>
<string name="title_app_shortcut_bookmark">Žymės</string>
<string name="title_app_shortcut_setting">Nustatymai</string>
<string name="remove_bookmark">Pašalinta iš žymių</string>
@ -524,12 +554,16 @@
<string name="wallpaper_set_unsuccessfully">Kažkas nepavyko. Nepavyko nustatyti fono paveikslėlio</string>
<string name="setting_wallpaper_dialog_title">Nustatyti kaip fono paveikslėlį</string>
<string name="setting_wallpaper_dialog_message">Fono paveikslėlis nustatomas. Prašome palaukti…</string>
<string name="theme_default_name">Sekti sistemos</string>
<string name="theme_dark_name">Tamsus</string>
<string name="theme_light_name">Šviesus</string>
<string name="cannot_open_location_settings">Nepavyko atidaryti vietos nustatymų. Įjunkite vietą rankiniu būdu</string>
<string name="recommend_high_accuracy_mode">Norėdami gauti geriausius rezultatus, pasirinkite didelio tikslumo režimą.</string>
<string name="ask_to_turn_location_on">Įjungti vietą?</string>
<string name="ask_to_turn_location_on_text">Įjunkite vietos nustatymo paslaugas, kad programa parodytų jūsų dabartinę vietą</string>
<string name="nearby_needs_location">Kad tinkamai veiktų, Netoliese turi būti įjungta vieta</string>
<string name="explore_map_needs_location">Žemėlapio naršymui reikia vietos leidimo, kad būtų rodomi netoliese esantys paveikslėliai</string>
<string name="upload_map_location_access">Norėdami automatiškai nustatyti vietą, turite suteikti vietos leidimą.</string>
<string name="use_location_from_similar_image">Ar nufotografavote šias dvi nuotraukas toje pačioje vietoje? Ar norite naudoti dešinėje esančio paveikslėlio platumą/ilgumą?</string>
<string name="load_more">Įkelti daugiau</string>
<string name="nearby_no_results">Vietų nerasta, pabandykite pakeisti paieškos kriterijus.</string>
@ -617,6 +651,10 @@
<string name="custom_selector_info_text2">Skirtingai nuo paveikslėlio kairėje, paveikslėlyje dešinėje yra Vikitekos logotipas, nurodantis, kad jis jau įkeltas. \n Palieskite ir palaikykite, kad peržiūrėtumėte vaizdą.</string>
<string name="welcome_custom_selector_ok">Puiku</string>
<string name="custom_selector_already_uploaded_image_text">Šis vaizdas jau buvo įkeltas į Vikiteką.</string>
<string name="custom_selector_over_limit_warning">Dėl techninių priežasčių programėlė negali patikimai įkelti daugiau nei %1$d nuotraukos vienu metu. %1$d įkėlimo limitas buvo viršytas %2$d.</string>
<string name="custom_selector_dismiss_limit_warning_button_text">Paslėpti</string>
<string name="custom_selector_button_limit_text">Maksimalus: %1$d</string>
<string name="custom_selector_limit_error_desc">Klaida: viršytas įkėlimo limitas</string>
<string name="wlm_upload_info">Šis vaizdas bus įtrauktas į konkursą \"Wiki Loves Monuments\" (\"Wiki\" mėgsta paminklus)</string>
<string name="display_monuments">Rodyti paminklus</string>
<string name="wlm_month_message">Vyksta Viki myli paminklus mėnuo!</string>
@ -626,7 +664,7 @@
<string name="read_phone_state_permission_message">Netoliese žemėlapiai turi perskaityti TELENFONO BŪSENĄ, kad tinkamai funkcionuotų</string>
<string name="contributions_of_user">Naudotojo indėlis: %s</string>
<string name="achievements_of_user">Naudotojo pasiekimai: %s</string>
<string name="menu_view_user_page" fuzzy="true">Žiūrėti naudotojo puslapį</string>
<string name="menu_view_user_page">Žiūrėti naudotojo puslapį</string>
<string name="edit_depictions">Redaguoti vaizdus</string>
<string name="edit_categories">Redaguoti kategorijas</string>
<string name="advanced_options">Išplėstiniai nustatymai</string>
@ -652,6 +690,8 @@
<string name="your_feedback">Jūsų atsiliepimas</string>
<string name="mark_as_not_for_upload">Pažymėti kaip neskirtą įkėlimui</string>
<string name="unmark_as_not_for_upload">Panaikinkite žymėjimą kaip neskirto įkėlimui</string>
<string name="marking_as_not_for_upload">Žymima kaip neįkėlimui</string>
<string name="unmarking_as_not_for_upload">Naikinamas žymėjimas kaip neįkėlimui</string>
<string name="show_already_actioned_pictures">Rodyti jau padarytas nuotraukas</string>
<string name="hiding_already_actioned_pictures">Slepiamos jau padarytos nuotraukos</string>
<string name="no_more_images_found">Daugiau paveikslėlių nerasta</string>
@ -660,6 +700,8 @@
<string name="image_selected">Paveiklėlis pasirinktas</string>
<string name="image_marked_as_not_for_upload">Paveikslėlis pažymėtas kaip neskirtas įkėlimui</string>
<string name="menu_view_report">Pranešti</string>
<string name="menu_view_set_white_background">Nustatyti baltą foną</string>
<string name="menu_view_set_black_background">Nustatyti juodą foną</string>
<string name="report_violation">Pranešti apie pažeidimą</string>
<string name="report_user">Pranešti apie šį nauodotoją</string>
<string name="report_content">Pranešti apie šį turinį</string>
@ -669,4 +711,44 @@
<string name="full_screen_mode_features_info">Norėdami atlikti šiuos veiksmus, braukite greitai ir ilgai: \n- Kairėn/dešinėn: Pereikite prie ankstesnio/kito\n- Į viršų: Pasirinkite\n- Žemyn: Pažymėti kaip neskirtą įkėlimui.</string>
<string name="set_up_avatar_toast_string">Norėdami nustatyti pirmaujančiųjų sąrašo avatarą, bet kurio vaizdo trijų taškų meniu palieskite „Nustatyti kaip avatarą“.</string>
<string name="similar_coordinate_description_auto_set">Koordinatės nėra tikslios koordinatės, bet asmuo, kuris įkėlė šią nuotrauką, mano, kad jos yra pakankamai arti.</string>
<string name="storage_permissions_denied">Saugyklos leidimai atmesti</string>
<string name="unable_to_share_upload_item">Nepavyko bendrinti šio elemento</string>
<string name="permissions_are_required_for_functionality">Funkcionalumui reikalingi leidimai</string>
<string name="learn_how_to_write_a_useful_description">Sužinokite, kaip parašyti naudingą aprašymą</string>
<string name="learn_how_to_write_a_useful_caption">Sužinokite, kaip parašyti naudingą antraštę</string>
<string name="see_your_achievements">Pamatykite savo pasiekimus</string>
<string name="edit_image">Redaguoti paveikslėlį</string>
<string name="edit_location">Redaguoti vietą</string>
<string name="location_updated">Vieta atnaujinta!</string>
<string name="remove_location">Pašalinti vietą</string>
<string name="remove_location_warning_title">Pašalinti vietos įspėjimą</string>
<string name="remove_location_warning_desc">Vieta daro nuotraukas naudingesnes ir lengviau randamas. Ar tikrai norite pašalinti vietą iš šios nuotraukos?</string>
<string name="location_removed">Vieta pašalinta!</string>
<string name="send_thanks_to_author">Padėkoti autoriui</string>
<string name="error_sending_thanks">Klaida siunčiant padėką autoriui.</string>
<string name="invalid_login_message">Jūsų prisijungimo sesija baigėsi. Prašome prisijungti dar kartą.</string>
<string name="no_application_available_to_open_gpx_files">Nėra jokios programos GPX failams atidaryti</string>
<string name="file_saved_successfully">Failas sėkmingai išsaugotas</string>
<string name="do_you_want_to_open_gpx_file">Ar norite atidaryti GPX failą?</string>
<string name="do_you_want_to_open_kml_file">Ar norite atidaryti KML failą?</string>
<string name="failed_to_save_kml_file">Nepavyko išsaugoti KML failo.</string>
<string name="failed_to_save_gpx_file">Nepavyko išsaugoti GPX failo.</string>
<string name="saving_kml_file">Išsaugomas KML failas</string>
<string name="saving_gpx_file">Išsaugomas GPX failas</string>
<string name="multiple_files_depiction">Atminkite, kad visi paveikslėliai, įkeliant kelis, turi tas pačias kategorijas ir vaizdus. Jei paveikslėliuose vaizdai ir kategorijos skiriasi, prašome atlikti kelis atskirus įkėlimus.</string>
<string name="multiple_files_depiction_header">Pastaba apie kelis įkėlimus</string>
<string name="nearby_wikitalk">Praneškite apie problemą dėl šio elemento Vikiduomenims.</string>
<string name="please_enter_some_comments">Įveskite keletą komentarų</string>
<string name="talk">Aptarimas</string>
<string name="write_something_about_the_item">Parašykite ką nors apie \'%1$s\' elementą. Tai bus matoma viešai.</string>
<string name="does_not_exist_anymore_no_picture_can_ever_be_taken_of_it">\'%1$s\' nebeegzistuoja, niekada nebegalima jo nufotografuoti.</string>
<string name="is_at_a_different_place_please_specify_the_correct_place_below_if_possible_tell_us_the_correct_latitude_longitude">\'%1$s\' yra kitoje vietoje. Toliau nurodykite teisingą vietą ir, jei įmanoma, parašykite teisingą platumą ir ilgumą.</string>
<string name="other_problem_or_information_please_explain_below">Kita problema arba informacija (paaiškinkite toliau).</string>
<string name="feedback_destination_note">Jūsų atsiliepimai bus paskelbti šiame viki puslapyje: &lt;a href=\"https://commons.wikimedia.org/wiki/Commons:Mobile_app/Feedback\"&gt;Commons:Mobile App/Feedback&lt;/a&gt;</string>
<string name="are_you_sure_that_you_want_cancel_all_the_uploads">Ar tikrai norite atšaukti visus įkėlimus?</string>
<string name="cancelling_all_the_uploads">Atšaukiami visi įkėlimai...</string>
<string name="uploads">Įkėlimai</string>
<string name="pending">Laukiama</string>
<string name="failed">Nepavyko</string>
<string name="could_not_load_place_data">Nepavyko įkelti vietos duomenų</string>
</resources>

View file

@ -475,6 +475,7 @@
<string name="no_notification">Немате непрочитани известувања</string>
<string name="no_read_notification">Немате прочитани известувања</string>
<string name="share_logs_using">Споделувај дневници користејќи</string>
<string name="check_your_email_inbox">Проверете си ја дојдовната е-пошта</string>
<string name="menu_option_read">Погл. прочитани</string>
<string name="menu_option_unread">Погл. непрочитани</string>
<string name="error_occurred_in_picking_images">Се јави грешка при избирањето на сликите</string>
@ -784,4 +785,7 @@
<string name="pending">Во исчекување</string>
<string name="failed">Неуспешно</string>
<string name="could_not_load_place_data">Не можев да ги вчитам податоците за место</string>
<string name="red_pin">Местово сè уште нема слика. Направете ја!</string>
<string name="green_pin">Местово веќе има слика.</string>
<string name="grey_pin">Проверувам дали местово има слика.</string>
</resources>

View file

@ -131,7 +131,7 @@
<string name="login_failed_network">Aanmelden niet mogelijk. Er is een probleem met het netwerk</string>
<string name="login_failed_throttled">Te veel mislukte pogingen. Probeer het over een paar minuten opnieuw.</string>
<string name="login_failed_blocked">Deze gebruiker is helaas geblokkeerd op Wikimedia Commons</string>
<string name="login_failed_2fa_needed">U moet uw code voor tweefactor-authenticatie opgeven.</string>
<string name="login_failed_2fa_needed">U moet uw code voor tweetrapsauthenticatie opgeven.</string>
<string name="login_failed_generic">Aanmelden mislukt</string>
<string name="share_upload_button">Uploaden</string>
<string name="multiple_share_base_title">Geef deze verzameling een naam</string>
@ -496,6 +496,7 @@
<string name="no_notification">U heeft geen ongelezen meldingen</string>
<string name="no_read_notification">U heeft geen gelezen meldingen</string>
<string name="share_logs_using">Logboeken delen via</string>
<string name="check_your_email_inbox">Bekijk uw e-mailinbox</string>
<string name="menu_option_read">Bekijk gelezen</string>
<string name="menu_option_unread">Ongelezen bekijken</string>
<string name="error_occurred_in_picking_images">Er is een fout opgetreden bij het kiezen van afbeeldingen</string>
@ -805,4 +806,7 @@
<string name="pending">In behandeling</string>
<string name="failed">Mislukt</string>
<string name="could_not_load_place_data">Plaatsgegevens konden niet geladen worden</string>
<string name="red_pin">Er is nog geen foto van deze plek, maak er eentje!</string>
<string name="green_pin">Er is al een foto van deze plek.</string>
<string name="grey_pin">We controleren nu of er een foto van deze plek is.</string>
</resources>

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