mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-30 22:34:02 +01:00 
			
		
		
		
	Merge branch 'main' into showMessageIfAllImagesUploadedOrMarked
This commit is contained in:
		
						commit
						1823f8359c
					
				
					 32 changed files with 344 additions and 236 deletions
				
			
		|  | @ -184,32 +184,32 @@ class LoginActivity : AccountAuthenticatorActivity() { | |||
|         // if progressDialog is visible during the configuration change  then store state as  true else false so that | ||||
|         // we maintain visibility of progressDialog after configuration change | ||||
|         if (progressDialog != null && progressDialog!!.isShowing) { | ||||
|             outState.putBoolean(saveProgressDialog, true) | ||||
|             outState.putBoolean(SAVE_PROGRESS_DIALOG, true) | ||||
|         } else { | ||||
|             outState.putBoolean(saveProgressDialog, false) | ||||
|             outState.putBoolean(SAVE_PROGRESS_DIALOG, false) | ||||
|         } | ||||
|         outState.putString( | ||||
|             saveErrorMessage, | ||||
|             SAVE_ERROR_MESSAGE, | ||||
|             binding!!.errorMessage.text.toString() | ||||
|         ) //Save the errorMessage | ||||
|         outState.putString( | ||||
|             saveUsername, | ||||
|             SAVE_USERNAME, | ||||
|             binding!!.loginUsername.text.toString() | ||||
|         ) // Save the username | ||||
|         outState.putString( | ||||
|             savePassword, | ||||
|             SAVE_PASSWORD, | ||||
|             binding!!.loginPassword.text.toString() | ||||
|         ) // Save the password | ||||
|     } | ||||
| 
 | ||||
|     override fun onRestoreInstanceState(savedInstanceState: Bundle) { | ||||
|         super.onRestoreInstanceState(savedInstanceState) | ||||
|         binding!!.loginUsername.setText(savedInstanceState.getString(saveUsername)) | ||||
|         binding!!.loginPassword.setText(savedInstanceState.getString(savePassword)) | ||||
|         if (savedInstanceState.getBoolean(saveProgressDialog)) { | ||||
|         binding!!.loginUsername.setText(savedInstanceState.getString(SAVE_USERNAME)) | ||||
|         binding!!.loginPassword.setText(savedInstanceState.getString(SAVE_PASSWORD)) | ||||
|         if (savedInstanceState.getBoolean(SAVE_PROGRESS_DIALOG)) { | ||||
|             performLogin() | ||||
|         } | ||||
|         val errorMessage = savedInstanceState.getString(saveErrorMessage) | ||||
|         val errorMessage = savedInstanceState.getString(SAVE_ERROR_MESSAGE) | ||||
|         if (sessionManager.isUserLoggedIn) { | ||||
|             showMessage(R.string.login_success, R.color.primaryDarkColor) | ||||
|         } else { | ||||
|  | @ -396,9 +396,9 @@ class LoginActivity : AccountAuthenticatorActivity() { | |||
|         fun startYourself(context: Context) = | ||||
|             context.startActivity(Intent(context, LoginActivity::class.java)) | ||||
| 
 | ||||
|         const val saveProgressDialog: String = "ProgressDialog_state" | ||||
|         const val saveErrorMessage: String = "errorMessage" | ||||
|         const val saveUsername: String = "username" | ||||
|         const val savePassword: String = "password" | ||||
|         const val SAVE_PROGRESS_DIALOG: String = "ProgressDialog_state" | ||||
|         const val SAVE_ERROR_MESSAGE: String = "errorMessage" | ||||
|         const val SAVE_USERNAME: String = "username" | ||||
|         const val SAVE_PASSWORD: String = "password" | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,23 +0,0 @@ | |||
| package fr.free.nrw.commons.concurrency; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import fr.free.nrw.commons.BuildConfig; | ||||
| 
 | ||||
| public class BackgroundPoolExceptionHandler implements ExceptionHandler { | ||||
|     /** | ||||
|      * If an exception occurs on a background thread, this handler will crash for debug builds | ||||
|      * but fail silently for release builds. | ||||
|      * @param t | ||||
|      */ | ||||
|     @Override | ||||
|     public void onException(@NonNull final Throwable t) { | ||||
|         //Crash for debug build | ||||
|         if (BuildConfig.DEBUG) { | ||||
|             Thread thread = new Thread(() -> { | ||||
|                 throw new RuntimeException(t); | ||||
|             }); | ||||
|             thread.start(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,21 @@ | |||
| package fr.free.nrw.commons.concurrency | ||||
| 
 | ||||
| import fr.free.nrw.commons.BuildConfig | ||||
| 
 | ||||
| 
 | ||||
| class BackgroundPoolExceptionHandler : ExceptionHandler { | ||||
|     /** | ||||
|      * If an exception occurs on a background thread, this handler will crash for debug builds | ||||
|      * but fail silently for release builds. | ||||
|      * @param t | ||||
|      */ | ||||
|     override fun onException(t: Throwable) { | ||||
|         // Crash for debug build | ||||
|         if (BuildConfig.DEBUG) { | ||||
|             val thread = Thread { | ||||
|                 throw RuntimeException(t) | ||||
|             } | ||||
|             thread.start() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,39 +0,0 @@ | |||
| package fr.free.nrw.commons.concurrency; | ||||
| 
 | ||||
| import java.util.concurrent.CancellationException; | ||||
| import java.util.concurrent.ExecutionException; | ||||
| import java.util.concurrent.Future; | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| import java.util.concurrent.ThreadFactory; | ||||
| 
 | ||||
| class ExceptionAwareThreadPoolExecutor extends ScheduledThreadPoolExecutor { | ||||
| 
 | ||||
|     private final ExceptionHandler exceptionHandler; | ||||
| 
 | ||||
|     public ExceptionAwareThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, | ||||
|                                             ExceptionHandler exceptionHandler) { | ||||
|         super(corePoolSize, threadFactory); | ||||
|         this.exceptionHandler = exceptionHandler; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void afterExecute(Runnable r, Throwable t) { | ||||
|         super.afterExecute(r, t); | ||||
|         if (t == null && r instanceof Future<?>) { | ||||
|             try { | ||||
|                 Future<?> future = (Future<?>) r; | ||||
|                 if (future.isDone()) future.get(); | ||||
|             } catch (CancellationException | InterruptedException e) { | ||||
|                 //ignore | ||||
|             } catch (ExecutionException e) { | ||||
|                 t = e.getCause() != null ? e.getCause() : e; | ||||
|             } catch (Exception e) { | ||||
|                 t = e; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (t != null) { | ||||
|             exceptionHandler.onException(t); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,40 @@ | |||
| package fr.free.nrw.commons.concurrency | ||||
| 
 | ||||
| import java.util.concurrent.CancellationException | ||||
| import java.util.concurrent.ExecutionException | ||||
| import java.util.concurrent.Future | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor | ||||
| import java.util.concurrent.ThreadFactory | ||||
| 
 | ||||
| 
 | ||||
| class ExceptionAwareThreadPoolExecutor( | ||||
|     corePoolSize: Int, | ||||
|     threadFactory: ThreadFactory, | ||||
|     private val exceptionHandler: ExceptionHandler? | ||||
| ) : ScheduledThreadPoolExecutor(corePoolSize, threadFactory) { | ||||
| 
 | ||||
|     override fun afterExecute(r: Runnable, t: Throwable?) { | ||||
|         super.afterExecute(r, t) | ||||
|         var throwable = t | ||||
| 
 | ||||
|         if (throwable == null && r is Future<*>) { | ||||
|             try { | ||||
|                 if (r.isDone) { | ||||
|                     r.get() | ||||
|                 } | ||||
|             } catch (e: CancellationException) { | ||||
|                 // ignore | ||||
|             } catch (e: InterruptedException) { | ||||
|                 // ignore | ||||
|             } catch (e: ExecutionException) { | ||||
|                 throwable = e.cause ?: e | ||||
|             } catch (e: Exception) { | ||||
|                 throwable = e | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         throwable?.let { | ||||
|             exceptionHandler?.onException(it) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,7 +0,0 @@ | |||
| package fr.free.nrw.commons.concurrency; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| public interface ExceptionHandler { | ||||
|     void onException(@NonNull Throwable t); | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| package fr.free.nrw.commons.concurrency | ||||
| 
 | ||||
| interface ExceptionHandler { | ||||
| 
 | ||||
|     fun onException(t: Throwable) | ||||
| 
 | ||||
| } | ||||
|  | @ -1,124 +0,0 @@ | |||
| package fr.free.nrw.commons.concurrency; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| import java.util.concurrent.Callable; | ||||
| import java.util.concurrent.Executor; | ||||
| import java.util.concurrent.ScheduledFuture; | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor; | ||||
| import java.util.concurrent.ThreadFactory; | ||||
| import java.util.concurrent.TimeUnit; | ||||
| 
 | ||||
| /** | ||||
|  * This class is a thread pool which provides some additional features: | ||||
|  * - it sets the thread priority to a value lower than foreground priority by default, or you can | ||||
|  * supply your own priority | ||||
|  * - it gives you a way to handle exceptions thrown in the thread pool | ||||
|  */ | ||||
| 
 | ||||
| public class ThreadPoolService implements Executor { | ||||
|     private final ScheduledThreadPoolExecutor backgroundPool; | ||||
| 
 | ||||
|     private ThreadPoolService(final Builder b) { | ||||
|         backgroundPool = new ExceptionAwareThreadPoolExecutor(b.poolSize, | ||||
|                 new ThreadFactory() { | ||||
|                     int count = 0; | ||||
|                     @Override | ||||
|                     public Thread newThread(@NonNull Runnable r) { | ||||
|                         count++; | ||||
|                         Thread t = new Thread(r, String.format("%s-%s", b.name, count)); | ||||
|                         //If the priority is specified out of range, we set the thread priority to Thread.MIN_PRIORITY | ||||
|                         //It's done prevent IllegalArgumentException and to prevent setting of improper high priority for a less priority task | ||||
|                         t.setPriority(b.priority > Thread.MAX_PRIORITY || b.priority < Thread.MIN_PRIORITY ? | ||||
|                                 Thread.MIN_PRIORITY : b.priority); | ||||
|                         return t; | ||||
|                     } | ||||
|                 }, b.exceptionHandler); | ||||
|     } | ||||
| 
 | ||||
|     public <V> ScheduledFuture<V> schedule(Callable<V> callable, long time, TimeUnit timeUnit) { | ||||
|         return backgroundPool.schedule(callable, time, timeUnit); | ||||
|     } | ||||
| 
 | ||||
|     public ScheduledFuture<?> schedule(Runnable runnable) { | ||||
|         return schedule(runnable, 0, TimeUnit.SECONDS); | ||||
|     } | ||||
| 
 | ||||
|     public ScheduledFuture<?> schedule(Runnable runnable, long time, TimeUnit timeUnit) { | ||||
|         return backgroundPool.schedule(runnable, time, timeUnit); | ||||
|     } | ||||
| 
 | ||||
|     public ScheduledFuture<?> scheduleAtFixedRate(final Runnable task, long initialDelay, | ||||
|                                                   long period, final TimeUnit timeUnit) { | ||||
|         return backgroundPool.scheduleAtFixedRate(task, initialDelay, period, timeUnit); | ||||
|     } | ||||
| 
 | ||||
|     public ScheduledThreadPoolExecutor executor() { | ||||
|         return backgroundPool; | ||||
|     } | ||||
| 
 | ||||
|     public void shutdown(){ | ||||
|         backgroundPool.shutdown(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void execute(Runnable command) { | ||||
|         backgroundPool.execute(command); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Builder class for {@link ThreadPoolService} | ||||
|      */ | ||||
|     public static class Builder { | ||||
|         //Required | ||||
|         private final String name; | ||||
| 
 | ||||
|         //Optional | ||||
|         private int poolSize = 1; | ||||
|         private int priority = Thread.MIN_PRIORITY; | ||||
|         private ExceptionHandler exceptionHandler = null; | ||||
| 
 | ||||
|         /** | ||||
|          * @param name the name of the threads in the service. if there are N threads, | ||||
|          *             the thread names will be like name-1, name-2, name-3,...,name-N | ||||
|          */ | ||||
|         public Builder(@NonNull String name) { | ||||
|             this.name = name; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param poolSize the number of threads to keep in the pool | ||||
|          * @throws IllegalArgumentException if size of pool <=0 | ||||
|          */ | ||||
|         public Builder setPoolSize(int poolSize) throws IllegalArgumentException { | ||||
|             if (poolSize <= 0) { | ||||
|                 throw new IllegalArgumentException("Pool size must be grater than 0"); | ||||
|             } | ||||
|             this.poolSize = poolSize; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param priority Priority of the threads in the service. You can supply a constant from | ||||
|          *                 {@link java.lang.Thread} or | ||||
|          *                 specify your own priority in the range 1(MIN_PRIORITY) to 10(MAX_PRIORITY) | ||||
|          *                 By default, the priority is set to {@link java.lang.Thread#MIN_PRIORITY} | ||||
|          */ | ||||
|         public Builder setPriority(int priority) { | ||||
|             this.priority = priority; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param handler The handler to use to handle exceptions in the service | ||||
|          */ | ||||
|         public Builder setExceptionHandler(ExceptionHandler handler) { | ||||
|             this.exceptionHandler = handler; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         public ThreadPoolService build() { | ||||
|             return new ThreadPoolService(this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,122 @@ | |||
| package fr.free.nrw.commons.concurrency | ||||
| 
 | ||||
| import java.util.concurrent.Callable | ||||
| import java.util.concurrent.Executor | ||||
| import java.util.concurrent.ScheduledFuture | ||||
| import java.util.concurrent.ScheduledThreadPoolExecutor | ||||
| import java.util.concurrent.ThreadFactory | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * This class is a thread pool which provides some additional features: | ||||
|  * - it sets the thread priority to a value lower than foreground priority by default, or you can | ||||
|  * supply your own priority | ||||
|  * - it gives you a way to handle exceptions thrown in the thread pool | ||||
|  */ | ||||
| class ThreadPoolService private constructor(builder: Builder) : Executor { | ||||
|     private val backgroundPool: ScheduledThreadPoolExecutor = ExceptionAwareThreadPoolExecutor( | ||||
|         builder.poolSize, | ||||
|         object : ThreadFactory { | ||||
|             private var count = 0 | ||||
|             override fun newThread(r: Runnable): Thread { | ||||
|                 count++ | ||||
|                 val t = Thread(r, "${builder.name}-$count") | ||||
|                 // If the priority is specified out of range, we set the thread priority to | ||||
|                 // Thread.MIN_PRIORITY | ||||
|                 // It's done to prevent IllegalArgumentException and to prevent setting of | ||||
|                 // improper high priority for a less priority task | ||||
|                 t.priority = | ||||
|                     if ( | ||||
|                         builder.priority > Thread.MAX_PRIORITY | ||||
|                         || | ||||
|                         builder.priority < Thread.MIN_PRIORITY | ||||
|                     ) { | ||||
|                     Thread.MIN_PRIORITY | ||||
|                 } else { | ||||
|                     builder.priority | ||||
|                 } | ||||
|                 return t | ||||
|             } | ||||
|         }, | ||||
|         builder.exceptionHandler | ||||
|     ) | ||||
| 
 | ||||
|     fun <V> schedule(callable: Callable<V>, time: Long, timeUnit: TimeUnit): ScheduledFuture<V> { | ||||
|         return backgroundPool.schedule(callable, time, timeUnit) | ||||
|     } | ||||
| 
 | ||||
|     fun schedule(runnable: Runnable): ScheduledFuture<*> { | ||||
|         return schedule(runnable, 0, TimeUnit.SECONDS) | ||||
|     } | ||||
| 
 | ||||
|     fun schedule(runnable: Runnable, time: Long, timeUnit: TimeUnit): ScheduledFuture<*> { | ||||
|         return backgroundPool.schedule(runnable, time, timeUnit) | ||||
|     } | ||||
| 
 | ||||
|     fun scheduleAtFixedRate( | ||||
|         task: Runnable, | ||||
|         initialDelay: Long, | ||||
|         period: Long, | ||||
|         timeUnit: TimeUnit | ||||
|     ): ScheduledFuture<*> { | ||||
|         return backgroundPool.scheduleWithFixedDelay(task, initialDelay, period, timeUnit) | ||||
|     } | ||||
| 
 | ||||
|     fun executor(): ScheduledThreadPoolExecutor { | ||||
|         return backgroundPool | ||||
|     } | ||||
| 
 | ||||
|     fun shutdown() { | ||||
|         backgroundPool.shutdown() | ||||
|     } | ||||
| 
 | ||||
|     override fun execute(command: Runnable) { | ||||
|         backgroundPool.execute(command) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Builder class for [ThreadPoolService] | ||||
|      */ | ||||
|     class Builder(val name: String) { | ||||
|         var poolSize: Int = 1 | ||||
|         var priority: Int = Thread.MIN_PRIORITY | ||||
|         var exceptionHandler: ExceptionHandler? = null | ||||
| 
 | ||||
|         /** | ||||
|          * @param poolSize the number of threads to keep in the pool | ||||
|          * @throws IllegalArgumentException if size of pool <= 0 | ||||
|          */ | ||||
|         fun setPoolSize(poolSize: Int): Builder { | ||||
|             if (poolSize <= 0) { | ||||
|                 throw IllegalArgumentException("Pool size must be greater than 0") | ||||
|             } | ||||
|             this.poolSize = poolSize | ||||
|             return this | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param priority Priority of the threads in the service. You can supply a constant from | ||||
|          *                 [java.lang.Thread] or | ||||
|          *                 specify your own priority in the range 1(MIN_PRIORITY) | ||||
|          *                 to 10(MAX_PRIORITY) | ||||
|          *                 By default, the priority is set to [java.lang.Thread.MIN_PRIORITY] | ||||
|          */ | ||||
|         fun setPriority(priority: Int): Builder { | ||||
|             this.priority = priority | ||||
|             return this | ||||
|         } | ||||
| 
 | ||||
|         /** | ||||
|          * @param handler The handler to use to handle exceptions in the service | ||||
|          */ | ||||
|         fun setExceptionHandler(handler: ExceptionHandler): Builder { | ||||
|             exceptionHandler = handler | ||||
|             return this | ||||
|         } | ||||
| 
 | ||||
|         fun build(): ThreadPoolService { | ||||
|             return ThreadPoolService(this) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -1,11 +1,7 @@ | |||
| package fr.free.nrw.commons.settings | ||||
| 
 | ||||
| object Prefs { | ||||
|     const val GLOBAL_PREFS = "fr.free.nrw.commons.preferences" | ||||
| 
 | ||||
|     const val TRACKING_ENABLED = "eventLogging" | ||||
|     const val DEFAULT_LICENSE = "defaultLicense" | ||||
|     const val UPLOADS_SHOWING = "uploadsShowing" | ||||
|     const val MANAGED_EXIF_TAGS = "managed_exif_tags" | ||||
|     const val DESCRIPTION_LANGUAGE = "languageDescription" | ||||
|     const val APP_UI_LANGUAGE = "appUiLanguage" | ||||
|  |  | |||
|  | @ -12,7 +12,6 @@ import fr.free.nrw.commons.theme.BaseActivity | |||
| class SettingsActivity : BaseActivity() { | ||||
| 
 | ||||
|     private lateinit var binding: ActivitySettingsBinding | ||||
| //    private var settingsDelegate: AppCompatDelegate? = null | ||||
| 
 | ||||
|     /** | ||||
|      * to be called when the activity starts | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| package fr.free.nrw.commons.settings | ||||
| 
 | ||||
| import android.Manifest.permission | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.app.Dialog | ||||
| import android.content.Context.MODE_PRIVATE | ||||
|  | @ -11,7 +10,6 @@ import android.net.Uri | |||
| import android.os.Bundle | ||||
| import android.text.Editable | ||||
| import android.text.TextWatcher | ||||
| import android.view.KeyEvent | ||||
| import android.view.View | ||||
| import android.widget.AdapterView | ||||
| import android.widget.Button | ||||
|  | @ -131,7 +129,7 @@ class SettingsFragment : PreferenceFragmentCompat() { | |||
|         inAppCameraLocationPref?.setOnPreferenceChangeListener { _, newValue -> | ||||
|             val isInAppCameraLocationTurnedOn = newValue as Boolean | ||||
|             if (isInAppCameraLocationTurnedOn) { | ||||
|                 createDialogsAndHandleLocationPermissions(requireActivity()) | ||||
|                 createDialogsAndHandleLocationPermissions() | ||||
|             } | ||||
|             true | ||||
|         } | ||||
|  | @ -256,7 +254,7 @@ class SettingsFragment : PreferenceFragmentCompat() { | |||
|      * | ||||
|      * @param activity | ||||
|      */ | ||||
|     private fun createDialogsAndHandleLocationPermissions(activity: Activity) { | ||||
|     private fun createDialogsAndHandleLocationPermissions() { | ||||
|         inAppCameraLocationPermissionLauncher.launch(arrayOf(permission.ACCESS_FINE_LOCATION)) | ||||
|     } | ||||
| 
 | ||||
|  | @ -284,7 +282,6 @@ class SettingsFragment : PreferenceFragmentCompat() { | |||
|         return object : PreferenceGroupAdapter(preferenceScreen) { | ||||
|             override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { | ||||
|                 super.onBindViewHolder(holder, position) | ||||
|                 val preference = getItem(position) | ||||
|                 val iconFrame: View? = holder.itemView.findViewById(R.id.icon_frame) | ||||
|                 iconFrame?.visibility = View.GONE | ||||
|             } | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| package fr.free.nrw.commons.upload | ||||
| 
 | ||||
| import android.content.ClipData | ||||
| import android.content.ClipboardManager | ||||
| import android.content.Context.CLIPBOARD_SERVICE | ||||
| import android.net.Uri | ||||
| import android.text.TextUtils | ||||
| import android.view.LayoutInflater | ||||
|  | @ -13,6 +16,7 @@ import androidx.paging.PagedListAdapter | |||
| import androidx.recyclerview.widget.DiffUtil | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.facebook.imagepipeline.request.ImageRequest | ||||
| import com.google.android.material.snackbar.Snackbar | ||||
| import fr.free.nrw.commons.R | ||||
| import fr.free.nrw.commons.contributions.Contribution | ||||
| import java.io.File | ||||
|  | @ -51,6 +55,24 @@ class FailedUploadsAdapter( | |||
|         position: Int, | ||||
|     ) { | ||||
|         val item: Contribution? = getItem(position) | ||||
|         val itemView = holder.itemView | ||||
|         val clipboardManager = | ||||
|             itemView.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager | ||||
| 
 | ||||
|         itemView.setOnLongClickListener { | ||||
|             val clip = ClipData.newPlainText( | ||||
|                 itemView.context.getString(R.string.caption), | ||||
|                 item?.media?.displayTitle | ||||
|             ) | ||||
|             clipboardManager.setPrimaryClip(clip) | ||||
|             Snackbar.make( | ||||
|                 itemView, | ||||
|                 itemView.context.getString(R.string.caption_copied_to_clipboard), | ||||
|                 Snackbar.LENGTH_SHORT | ||||
|             ).show() | ||||
|             true | ||||
|         } | ||||
| 
 | ||||
|         if (item != null) { | ||||
|             holder.titleTextView.setText(item.media.displayTitle) | ||||
|         } | ||||
|  |  | |||
|  | @ -49,9 +49,10 @@ class FileUtilsWrapper @Inject constructor(private val context: Context) { | |||
|                 while ((bis.read(buffer).also { size = it }) > 0) { | ||||
|                     buffers.add( | ||||
|                         writeToFile( | ||||
|                             buffer.copyOf(size), | ||||
|                             buffer, | ||||
|                             file.name ?: "", | ||||
|                             getFileExt(file.name) | ||||
|                             getFileExt(file.name), | ||||
|                             size | ||||
|                         ) | ||||
|                     ) | ||||
|                 } | ||||
|  | @ -67,7 +68,7 @@ class FileUtilsWrapper @Inject constructor(private val context: Context) { | |||
|      * Create a temp file containing the passed byte data. | ||||
|      */ | ||||
|     @Throws(IOException::class) | ||||
|     private fun writeToFile(data: ByteArray, fileName: String, fileExtension: String): File { | ||||
|     private fun writeToFile(data: ByteArray, fileName: String, fileExtension: String, size: Int): File { | ||||
|         val file = File.createTempFile(fileName, fileExtension, context.cacheDir) | ||||
|         try { | ||||
|             if (!file.exists()) { | ||||
|  | @ -75,7 +76,7 @@ class FileUtilsWrapper @Inject constructor(private val context: Context) { | |||
|             } | ||||
| 
 | ||||
|             FileOutputStream(file).use { fos -> | ||||
|                 fos.write(data) | ||||
|                 fos.write(data, 0, size) | ||||
|             } | ||||
|         } catch (throwable: Exception) { | ||||
|             Timber.e(throwable, "Failed to create file") | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| package fr.free.nrw.commons.upload | ||||
| 
 | ||||
| import android.content.ClipData | ||||
| import android.content.ClipboardManager | ||||
| import android.content.Context.CLIPBOARD_SERVICE | ||||
| import android.net.Uri | ||||
| import android.text.TextUtils | ||||
| import android.view.LayoutInflater | ||||
|  | @ -13,6 +16,7 @@ import androidx.paging.PagedListAdapter | |||
| import androidx.recyclerview.widget.DiffUtil | ||||
| import androidx.recyclerview.widget.RecyclerView | ||||
| import com.facebook.imagepipeline.request.ImageRequest | ||||
| import com.google.android.material.snackbar.Snackbar | ||||
| import fr.free.nrw.commons.R | ||||
| import fr.free.nrw.commons.contributions.Contribution | ||||
| import java.io.File | ||||
|  | @ -99,6 +103,22 @@ class PendingUploadsAdapter( | |||
| 
 | ||||
|         fun bind(contribution: Contribution) { | ||||
|             titleTextView.text = contribution.media.displayTitle | ||||
|             val clipboardManager = | ||||
|                 itemView.context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager | ||||
| 
 | ||||
|             itemView.setOnLongClickListener { | ||||
|                 val clip = ClipData.newPlainText( | ||||
|                     itemView.context.getString(R.string.caption), | ||||
|                     titleTextView.text | ||||
|                 ) | ||||
|                 clipboardManager.setPrimaryClip(clip) | ||||
|                 Snackbar.make( | ||||
|                     itemView, | ||||
|                     itemView.context.getString(R.string.caption_copied_to_clipboard), | ||||
|                     Snackbar.LENGTH_SHORT | ||||
|                 ).show() | ||||
|                 true | ||||
|             } | ||||
| 
 | ||||
|             val imageSource: String = contribution.localUri.toString() | ||||
|             var imageRequest: ImageRequest? = null | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ class MwQueryPage : BaseModel() { | |||
| 
 | ||||
|     fun title(): String = title!! | ||||
| 
 | ||||
|     fun categoryInfo(): CategoryInfo = categoryinfo!! | ||||
|     fun categoryInfo(): CategoryInfo? = categoryinfo | ||||
| 
 | ||||
|     fun index(): Int = index | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ | |||
|         android:layout_width="@dimen/landscape_width" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="center" | ||||
|         android:layout_marginTop="@dimen/small_gap"> | ||||
|         android:layout_marginTop="@dimen/login_gap"> | ||||
| 
 | ||||
|         <androidx.cardview.widget.CardView | ||||
|             android:layout_width="match_parent" | ||||
|  |  | |||
|  | @ -2,12 +2,14 @@ | |||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent"> | ||||
|     android:layout_height="match_parent" | ||||
|     android:layout_gravity="center_vertical"> | ||||
| 
 | ||||
|     <LinearLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:orientation="vertical"> | ||||
|         android:orientation="vertical" | ||||
|         android:layout_gravity="center_vertical"> | ||||
| 
 | ||||
|         <FrameLayout | ||||
|             android:layout_width="@dimen/landscape_width" | ||||
|  |  | |||
|  | @ -1,13 +1,15 @@ | |||
| <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|   android:layout_width="wrap_content" | ||||
|   android:layout_height="wrap_content"> | ||||
|   android:layout_height="wrap_content" | ||||
|   android:layout_gravity="center_vertical"> | ||||
| 
 | ||||
|   <LinearLayout 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:fillViewport="true" | ||||
|     android:orientation="vertical"> | ||||
|     android:orientation="vertical" | ||||
|     android:layout_gravity="center_vertical"> | ||||
| 
 | ||||
|     <FrameLayout | ||||
|       android:layout_width="wrap_content" | ||||
|  |  | |||
|  | @ -56,8 +56,8 @@ | |||
|   <string name="login_success">Uğurlu giriş!</string> | ||||
|   <string name="login_failed">Giriş baş tutmadı!</string> | ||||
|   <string name="upload_failed">Fayl tapılmadı. Xahiş edirik başqa bir fayl üzərində cəhd edin.</string> | ||||
|   <string name="retry_limit_reached">Maksimum təkrar cəhd limitinə çatdınız! Yükləməni ləğv edin və yenidən cəhd edin</string> | ||||
|   <string name="unrestricted_battery_mode">Batareya optimallaşdırılmasını söndürmək?</string> | ||||
|   <string name="retry_limit_reached">Maksimum təkrar cəhd limitinə çatdınız! Zəhmət olmasa, yükləməni ləğv edin və yenidən cəhd edin</string> | ||||
|   <string name="unrestricted_battery_mode">Batareya optimallaşdırılması söndürülsün?</string> | ||||
|   <string name="authentication_failed" fuzzy="true">Doğrulama alınmadı, xahiş edirəm yenidən daxil olun</string> | ||||
|   <string name="uploading_started">Yükləmə başladı!</string> | ||||
|   <string name="upload_completed_notification_title">%1$s yükləndi!</string> | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ | |||
| * Reedy | ||||
| * RucolaSpacecat | ||||
| * Sebastian Wallroth | ||||
| * SergeCroise | ||||
| * SpaceEnergy | ||||
| * Sujan | ||||
| * Sushi | ||||
|  | @ -313,6 +314,7 @@ | |||
|   <string name="copy_wikicode">Den Wikitext in die Zwischenablage kopieren</string> | ||||
|   <string name="wikicode_copied">Der Wikitext wurde in die Zwischenablage kopiert</string> | ||||
|   <string name="nearby_location_not_available">„In der Nähe“ arbeiten möglicherwiese nicht richtig. Der Standort ist nicht verfügbar.</string> | ||||
|   <string name="nearby_showing_pins_offline">Internet ist nicht verfügbar. Es werden nur zwischengespeicherte Orte angezeigt.</string> | ||||
|   <string name="upload_location_access_denied">Standortzugriff verweigert. Bitte lege deinen Standort manuell fest, um diese Funktion nutzen zu können.</string> | ||||
|   <string name="location_permission_rationale_nearby">Berechtigung zur Anzeige einer Liste mit Orten in der Nähe erforderlich</string> | ||||
|   <string name="location_permission_rationale_explore">Berechtigung zur Anzeige einer Liste mit Orten in der Nähe erforderlich</string> | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| * Astralnet | ||||
| * Domdomegg | ||||
| * Evropi | ||||
| * Fotis A. | ||||
| * Geraki | ||||
| * Giannaras99 | ||||
| * Giorgos456 | ||||
|  | @ -296,6 +297,7 @@ | |||
|   <string name="copy_wikicode">Αντιγράψτε το wikitext στο πρόχειρο</string> | ||||
|   <string name="wikicode_copied">Το wikitext αντιγράφηκε στο πρόχειρο</string> | ||||
|   <string name="nearby_location_not_available">Η λειτουργία «Κοντά σας» ενδέχεται να μη δουλεύει σωστά, η Τοποθεσία δεν είναι διαθέσιμη.</string> | ||||
|   <string name="nearby_showing_pins_offline">Το Διαδίκτυο δεν είναι διαθέσιμο. Εμφάνιση μόνο αποθηκευμένων τοποθεσιών.</string> | ||||
|   <string name="upload_location_access_denied">Δεν επιτρέπεται η πρόσβαση στην τοποθεσία. Ρυθμίστε την τοποθεσία σας με χειροκίνητο τρόπο για να χρησιμοποιήσετε αυτήν τη δυνατότητα.</string> | ||||
|   <string name="location_permission_rationale_nearby">Απαιτείται άδεια για την εμφάνιση καταλόγου κοντινών σημείων</string> | ||||
|   <string name="location_permission_rationale_explore">Απαιτείται άδεια για την εμφάνιση καταλόγου κοντινών εικόνων</string> | ||||
|  |  | |||
|  | @ -100,6 +100,8 @@ | |||
|   <string name="menu_from_camera">Prender photo</string> | ||||
|   <string name="menu_nearby">A proximitate</string> | ||||
|   <string name="provider_contributions">Mi incargamentos</string> | ||||
|   <string name="menu_copy_link">Copiar ligamine</string> | ||||
|   <string name="menu_link_copied">Le ligamine ha essite copiate al area de transferentia</string> | ||||
|   <string name="menu_share">Condivider</string> | ||||
|   <string name="menu_view_file_page">Visitar le pagina del file</string> | ||||
|   <string name="share_title_hint">Legenda (obligatori)</string> | ||||
|  | @ -118,6 +120,7 @@ | |||
|   <string name="categories_search_text_hint">Cercar categorias</string> | ||||
|   <string name="depicts_search_text_hint">Cerca elementos que tu file representa (montania, Taj Mahal, etc.)</string> | ||||
|   <string name="menu_save_categories">Salveguardar</string> | ||||
|   <string name="menu_overflow_desc">Menu de disbordamento</string> | ||||
|   <string name="refresh_button">Refrescar</string> | ||||
|   <string name="display_list_button">Listar</string> | ||||
|   <string name="contributions_subtitle_zero">(Nihil incargate ancora)</string> | ||||
|  | @ -269,6 +272,7 @@ | |||
|   <string name="copy_wikicode">Copiar le wikitexto al area de transferentia</string> | ||||
|   <string name="wikicode_copied">Le wikitexto ha essite copiate al area de transferentia</string> | ||||
|   <string name="nearby_location_not_available">“A proximitate” poterea non functionar perque le localisation non es disponibile.</string> | ||||
|   <string name="nearby_showing_pins_offline">Internet indisponibile. Appare solmente le locos in cache.</string> | ||||
|   <string name="upload_location_access_denied">Le accesso al localisation ha essite refusate. Per favor indica tu localisation manualmente pro usar iste function.</string> | ||||
|   <string name="location_permission_rationale_nearby">Permission necessari pro monstrar un lista de locos a proximitate</string> | ||||
|   <string name="location_permission_rationale_explore">Permission necessari pro monstrar un lista de imagines a proximitate</string> | ||||
|  | @ -352,11 +356,13 @@ | |||
|   <string name="delete">Deler</string> | ||||
|   <string name="Achievements">Realisationes</string> | ||||
|   <string name="Profile">Profilo</string> | ||||
|   <string name="badges">Insignias</string> | ||||
|   <string name="statistics">Statisticas</string> | ||||
|   <string name="statistics_thanks">Regratiamentos recipite</string> | ||||
|   <string name="statistics_featured">Imagines eminente</string> | ||||
|   <string name="statistics_wikidata_edits">Imagines via “Locos a proximitate”</string> | ||||
|   <string name="level" fuzzy="true">Nivello</string> | ||||
|   <string name="level">Nivello %d</string> | ||||
|   <string name="profileLevel">%s (Nivello %s)</string> | ||||
|   <string name="images_uploaded">Imagines incargate</string> | ||||
|   <string name="image_reverts">Imagines non revertite</string> | ||||
|   <string name="images_used_by_wiki">Imagines usate</string> | ||||
|  | @ -388,6 +394,7 @@ | |||
|   <string name="map_application_missing">Necun application cartographic compatibile pote esser trovate sur tu apparato. Per favor installa un application cartographic pro usar iste function.</string> | ||||
|   <string name="title_page_bookmarks_pictures">Imagines</string> | ||||
|   <string name="title_page_bookmarks_locations">Locos</string> | ||||
|   <string name="title_page_bookmarks_categories">Categorias</string> | ||||
|   <string name="menu_bookmark">Adder al/Remover del marcapaginas</string> | ||||
|   <string name="provider_bookmarks">Marcapaginas</string> | ||||
|   <string name="bookmark_empty">Tu non ha addite alcun marcapagina</string> | ||||
|  | @ -469,6 +476,7 @@ | |||
|   <string name="no_notification">Tu non ha notificationes non legite</string> | ||||
|   <string name="no_read_notification">Tu non ha notificationes legite</string> | ||||
|   <string name="share_logs_using">Condivider registros usante</string> | ||||
|   <string name="check_your_email_inbox">Consulta tu cassa de entrata</string> | ||||
|   <string name="menu_option_read">Vider legites</string> | ||||
|   <string name="menu_option_unread">Vider non legites</string> | ||||
|   <string name="error_occurred_in_picking_images">Un error ha occurrite durante le selection de imagines</string> | ||||
|  | @ -776,4 +784,21 @@ | |||
|   <string name="pending">Pendente</string> | ||||
|   <string name="failed">Fallite</string> | ||||
|   <string name="could_not_load_place_data">Non poteva cargar le datos del loco</string> | ||||
|   <string name="custom_selector_delete_folder">Deler dossier</string> | ||||
|   <string name="custom_selector_confirm_deletion_title">Confirmar deletion</string> | ||||
|   <string name="custom_selector_confirm_deletion_message">Es tu secur de voler deler le dossier %1$s que contine %2$d objectos?</string> | ||||
|   <string name="custom_selector_delete">Deler</string> | ||||
|   <string name="custom_selector_cancel">Cancellar</string> | ||||
|   <string name="custom_selector_folder_deleted_success">Le dossier %1$s ha essite delite</string> | ||||
|   <string name="custom_selector_folder_deleted_failure">Le dossier %1$s non ha potite esser delite</string> | ||||
|   <string name="custom_selector_error_trashing_folder_contents">Error durante le elimination del contento del dossier: %1$s</string> | ||||
|   <string name="custom_selector_folder_not_found_error">Non poteva recuperar le percurso al dossier pro le “bucket ID” %1$d</string> | ||||
|   <string name="red_pin">Iste loco non ha ancora un photo. Proque non prender un?</string> | ||||
|   <string name="green_pin">Iste loco ja ha un photo.</string> | ||||
|   <string name="grey_pin">Ora se verifica si iste loco ha un photo.</string> | ||||
|   <string name="error_while_loading">Error durante le cargamento</string> | ||||
|   <string name="no_usages_found">Necun uso trovate</string> | ||||
|   <string name="usages_on_commons_heading">Commons</string> | ||||
|   <string name="usages_on_other_wikis_heading">Altere wikis</string> | ||||
|   <string name="file_usages_container_heading">Usos del file</string> | ||||
| </resources> | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- Authors: | ||||
| * A67-A67 | ||||
| * ABPMAB | ||||
| * Bouman4 | ||||
| * Ciell | ||||
| * Dutchy45 | ||||
|  | @ -299,6 +300,7 @@ | |||
|   <string name="copy_wikicode">Kopieer de wikitekst naar het klembord</string> | ||||
|   <string name="wikicode_copied">De wikitekst is gekopieerd naar het klembord</string> | ||||
|   <string name="nearby_location_not_available">In de buurt werkt mogelijk niet naar behoren. Locatie niet beschikbaar.</string> | ||||
|   <string name="nearby_showing_pins_offline">Internet niet beschikbaar. Alleen gecachte plaatsen worden getoond.</string> | ||||
|   <string name="upload_location_access_denied">Locatietoegang geweigerd. Stel uw locatie handmatig in om deze functie te gebruiken.</string> | ||||
|   <string name="location_permission_rationale_nearby">Toestemming vereist om een lijst van nabije plaatsen weer te geven</string> | ||||
|   <string name="location_permission_rationale_explore">Toestemming vereist om een lijst van nabije afbeeldingen weer te geven</string> | ||||
|  |  | |||
|  | @ -119,7 +119,7 @@ | |||
|   <string name="license_name_cc_by_4_0">CC BY 4.0</string> | ||||
|   <string name="tutorial_1_text">ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਜ਼ਿਆਦਾਤਰ ਉਹ ਤਸਵੀਰਾਂ ਦਾ ਭੰਡਾਰ ਹੈ ਜੋ ਵਿਕੀਪੀਡੀਆ \'ਤੇ ਵਰਤੀਆਂ ਜਾਂਦੀਆਂ ਹਨ।</string> | ||||
|   <string name="tutorial_1_subtext">ਤੁਹਾਡੀਆਂ ਤਸਵੀਰਾਂ ਵਿਸ਼ਵ ਦੇ ਬਾਕੀ ਲੋਕਾਂ ਨੂੰ ਸਿੱਖਿਅਤ ਕਰਨ ਲਈ ਸਹਾਈ ਹਨ!</string> | ||||
|   <string name="tutorial_2_text">ਕਿਰਪਾ ਕਰਕੇ ਉਹ ਤਸਵੀਰਾਂ ਅਪਲੋਡ ਕਰੋ ਜੋ ਤੁਹਾਡੇ ਦੁਆਰਾ ਲਈਆਂ ਗਈਆਂ ਹਨ ਜਾਂ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ:</string> | ||||
|   <string name="tutorial_2_text">ਕਿਰਪਾ ਕਰਕੇ ਉਹ ਤਸਵੀਰਾਂ  ਨੂੰ ਚੜ੍ਹਾਉ ਜੋ ਤੁਹਾਡੇ ਵੱਲੋਂ ਲਈਆਂ ਗਈਆਂ ਹਨ ਜਾਂ ਬਣਾਈਆਂ ਗਈਆਂ ਹਨ:</string> | ||||
|   <string name="tutorial_3_text">ਕਿਰਪਾ ਕਰਕੇ ਅਪਲੋਡ ਨਾ ਕਰੋ:</string> | ||||
|   <string name="tutorial_4_text">ਉਦਾਹਰਣ ਵਜੋਂ ਇਹ ਅਪਲੋਡ:</string> | ||||
|   <string name="welcome_wikipedia_text">ਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਦਾ ਯੋਗਦਾਨ ਪਾਓ। ਵਿਕੀਪੀਡੀਆ ਲੇਖਾਂ ਨੂੰ ਸੁਰਜੀਤ ਕਰ ਦਿਓ!</string> | ||||
|  |  | |||
|  | @ -144,7 +144,7 @@ | |||
|   <string name="welcome_help_button_text">Lassedieđut</string> | ||||
|   <string name="detail_panel_cats_label">Kategoriijat</string> | ||||
|   <string name="detail_panel_cats_loading">Luđeme...</string> | ||||
|   <string name="detail_panel_cats_none">Ii guhtege válljejuvvon</string> | ||||
|   <string name="detail_panel_cats_none">Ii oktage válljejuvvon</string> | ||||
|   <string name="detail_caption_empty">Ii leat govvateaksta</string> | ||||
|   <string name="detail_description_empty">Ii gávdno govvádus</string> | ||||
|   <string name="detail_discussion_empty">Ii digaštallojuvvo</string> | ||||
|  | @ -217,6 +217,10 @@ | |||
|   <string name="next">Čuovvovaš</string> | ||||
|   <string name="title_page_bookmarks_pictures">Govat</string> | ||||
|   <string name="title_page_bookmarks_locations">Sajit</string> | ||||
|   <string name="provider_bookmarks">Girjemearkkat</string> | ||||
|   <string name="provider_bookmarks_location">Girjemearkkat</string> | ||||
|   <string name="no_categories_selected">Ii oktage kategoriija leat válljejuvvon</string> | ||||
|   <string name="no_depictions_selected">Ii oktage govvádus leat válljejuvvon</string> | ||||
|   <string name="back_button_warning">Gaskkalduhte fiilla vurkema</string> | ||||
|   <string name="review_thanks_yes_button_text">Čuovvovaš govva</string> | ||||
|   <string name="exif_tag_name_author">Vuoigŋadahkki</string> | ||||
|  | @ -230,9 +234,11 @@ | |||
|   <string name="caption_edit_helper_edit_message_else">Govvateavstta lasiheapmi ii lihkostuvvan.</string> | ||||
|   <string name="description_activity_title">Rievdat govvádusaid ja govvateavsttaid</string> | ||||
|   <string name="nearby_search_hint">Šaldi, musea, hotealla jna.</string> | ||||
|   <string name="title_app_shortcut_bookmark">Girjemearkkat</string> | ||||
|   <string name="load_more">Luđe lasi</string> | ||||
|   <string name="add_picture_to_wikipedia_article_title">Lasit gova Wikipediai</string> | ||||
|   <string name="resume">joatkke</string> | ||||
|   <string name="bookmarks">Girjemearkkat</string> | ||||
|   <string name="leaderboard_column_user">Geavaheaddji</string> | ||||
|   <string name="cancelling_upload">Fiilla vurkema gaskkalduhttomin…</string> | ||||
|   <string name="license_step_title">Medialiseansa</string> | ||||
|  |  | |||
|  | @ -158,7 +158,7 @@ | |||
|   <string name="storage_permission_title">ಶೇಖರಣ ಅನುಮತಿ ಕೇಣುನು</string> | ||||
|   <string name="read_storage_permission_rationale">ಅಗತ್ಯ ಇಪ್ಪುನ ಅನುಮತಿ: ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆನ್ ಓದಿ. ಇದೆಂಡ್ತ್ ಇರ್ ಗ್ಯಾಲರಿಗ್ ಅಪ್ಲಿಕೇಶನ್ ಪ್ರವೇಶಗ್ ಸಾಧ್ಯ ಇಜ್ಜಿ.</string> | ||||
|   <string name="write_storage_permission_rationale" fuzzy="true">ಅಗತ್ಯ ಇಪ್ಪುನ ಅನುಮತಿ: ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆನ್ ಬರೆಲೆ. ಇದೆಂಡ್ತ್ ಅಪ್ಲಿಕೇಶನ್ ಇರೆನ ಕ್ಯಾಮರೊನ್ ಪ್ರವೇಶ ಮಾಲ್ಪಯರ ಸಾಧ್ಯಿಜ್ಜಿ.</string> | ||||
|   <string name="ok">ಅವು</string> | ||||
|   <string name="ok">ಆವು</string> | ||||
|   <string name="warning">ಎಚ್ಚರಿಕೆ</string> | ||||
|   <string name="yes">ಅಂದ್</string> | ||||
|   <string name="no">ಅತ್ತ್</string> | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| * Simon Shek | ||||
| * StephDC | ||||
| * Tranve | ||||
| * Wehwei | ||||
| * Winston Sung | ||||
| * Wwycheuk | ||||
| * ZhaoGang | ||||
|  | @ -122,6 +123,8 @@ | |||
|   <string name="menu_from_camera">拍照</string> | ||||
|   <string name="menu_nearby">附近</string> | ||||
|   <string name="provider_contributions">我的上傳</string> | ||||
|   <string name="menu_copy_link">複製連結</string> | ||||
|   <string name="menu_link_copied">連結已複製到剪貼簿。</string> | ||||
|   <string name="menu_share">分享</string> | ||||
|   <string name="menu_view_file_page">檢視檔案頁面</string> | ||||
|   <string name="share_title_hint">說明(必填)</string> | ||||
|  | @ -140,6 +143,7 @@ | |||
|   <string name="categories_search_text_hint">搜尋分類</string> | ||||
|   <string name="depicts_search_text_hint">搜尋您的媒體描繪項目(山、泰姬瑪哈陵等)</string> | ||||
|   <string name="menu_save_categories">儲存</string> | ||||
|   <string name="menu_overflow_desc">溢出選單</string> | ||||
|   <string name="refresh_button">重新整理</string> | ||||
|   <string name="display_list_button">清單</string> | ||||
|   <string name="contributions_subtitle_zero">(尚未上傳)</string> | ||||
|  | @ -296,6 +300,7 @@ | |||
|   <string name="copy_wikicode">複製 wikitext 到剪貼簿</string> | ||||
|   <string name="wikicode_copied">Wikitext 已複製到剪貼簿</string> | ||||
|   <string name="nearby_location_not_available">位置無效,應用程式在此地附近可能無法正常運作。</string> | ||||
|   <string name="nearby_showing_pins_offline">網路無法使用。僅顯示快取位置。</string> | ||||
|   <string name="upload_location_access_denied">位置存取遭拒。請手動設定您的位置才能使用此功能。</string> | ||||
|   <string name="location_permission_rationale_nearby">需權限來顯示附近地點清單</string> | ||||
|   <string name="location_permission_rationale_explore">需要權限來顯示附近圖片清單</string> | ||||
|  | @ -379,11 +384,13 @@ | |||
|   <string name="delete">刪除</string> | ||||
|   <string name="Achievements">成果</string> | ||||
|   <string name="Profile">個人檔案</string> | ||||
|   <string name="badges">徽章:</string> | ||||
|   <string name="statistics">統計內容</string> | ||||
|   <string name="statistics_thanks">已收到的感謝</string> | ||||
|   <string name="statistics_featured">特色圖片</string> | ||||
|   <string name="statistics_wikidata_edits">圖片來自“附近的地方”</string> | ||||
|   <string name="level" fuzzy="true">等級</string> | ||||
|   <string name="level">等級 %d</string> | ||||
|   <string name="profileLevel">%s(%s級)</string> | ||||
|   <string name="images_uploaded">已上傳的圖片</string> | ||||
|   <string name="image_reverts">沒有被還原回復的圖片</string> | ||||
|   <string name="images_used_by_wiki">有被使用到的圖片</string> | ||||
|  | @ -415,6 +422,7 @@ | |||
|   <string name="map_application_missing">在您的裝置上找不到相容的地圖應用程式,請安裝地圖應用程式來使用此功能。</string> | ||||
|   <string name="title_page_bookmarks_pictures">圖片</string> | ||||
|   <string name="title_page_bookmarks_locations">位置</string> | ||||
|   <string name="title_page_bookmarks_categories">分類</string> | ||||
|   <string name="menu_bookmark">添加/移除至書籤</string> | ||||
|   <string name="provider_bookmarks">書籤</string> | ||||
|   <string name="bookmark_empty">您尚未添加任何書籤</string> | ||||
|  | @ -806,7 +814,21 @@ | |||
|   <string name="pending">待處理</string> | ||||
|   <string name="failed">失敗</string> | ||||
|   <string name="could_not_load_place_data">無法載入地點資料</string> | ||||
|   <string name="custom_selector_delete_folder">刪除資料夾</string> | ||||
|   <string name="custom_selector_confirm_deletion_title">確認刪除</string> | ||||
|   <string name="custom_selector_confirm_deletion_message">您確定要刪除包含%2$d項目的資料夾%1$s嗎?</string> | ||||
|   <string name="custom_selector_delete">刪除</string> | ||||
|   <string name="custom_selector_cancel">取消</string> | ||||
|   <string name="custom_selector_folder_deleted_success">%1$s資料夾刪除成功</string> | ||||
|   <string name="custom_selector_folder_deleted_failure">無法刪除資料夾%1$s</string> | ||||
|   <string name="custom_selector_error_trashing_folder_contents">刪除資料夾內容時發生錯誤: %1$s</string> | ||||
|   <string name="custom_selector_folder_not_found_error">無法取得儲存桶 ID 的資料夾路徑: %1$d</string> | ||||
|   <string name="red_pin">這個地點還沒有照片,來拍一張吧!</string> | ||||
|   <string name="green_pin">這個地點已有照片。</string> | ||||
|   <string name="grey_pin">現在檢查這個地點是否有照片。</string> | ||||
|   <string name="error_while_loading">載入時出錯</string> | ||||
|   <string name="no_usages_found">找不到頁面。</string> | ||||
|   <string name="usages_on_commons_heading">維基共享資源</string> | ||||
|   <string name="usages_on_other_wikis_heading">其它 wiki</string> | ||||
|   <string name="file_usages_container_heading">檔案用途</string> | ||||
| </resources> | ||||
|  |  | |||
|  | @ -154,6 +154,8 @@ | |||
|   <string name="menu_from_camera">拍照</string> | ||||
|   <string name="menu_nearby">附近</string> | ||||
|   <string name="provider_contributions">我的上传</string> | ||||
|   <string name="menu_copy_link">复制链接</string> | ||||
|   <string name="menu_link_copied">链接已复制到剪贴板。</string> | ||||
|   <string name="menu_share">分享</string> | ||||
|   <string name="menu_view_file_page">查看文件页面</string> | ||||
|   <string name="share_title_hint">说明(必需)</string> | ||||
|  | @ -329,6 +331,7 @@ | |||
|   <string name="copy_wikicode">复制wikitext到剪贴板</string> | ||||
|   <string name="wikicode_copied">wikitext已经复制到剪贴板</string> | ||||
|   <string name="nearby_location_not_available">附近可能无法正常工作,位置不可用。</string> | ||||
|   <string name="nearby_showing_pins_offline">互联网不可用。仅显示缓存的位置。</string> | ||||
|   <string name="upload_location_access_denied">位置权限被拒绝。请手动设置您的位置以使用此功能。</string> | ||||
|   <string name="location_permission_rationale_nearby">需要权限以显示附近地点列表</string> | ||||
|   <string name="location_permission_rationale_explore">需要权限以显示附近图片列表</string> | ||||
|  | @ -412,11 +415,13 @@ | |||
|   <string name="delete">删除</string> | ||||
|   <string name="Achievements">成就</string> | ||||
|   <string name="Profile">个人资料</string> | ||||
|   <string name="badges">徽章</string> | ||||
|   <string name="statistics">统计</string> | ||||
|   <string name="statistics_thanks">已收到感谢</string> | ||||
|   <string name="statistics_featured">特色图片</string> | ||||
|   <string name="statistics_wikidata_edits">来自“附近地点”的图片</string> | ||||
|   <string name="level" fuzzy="true">等级</string> | ||||
|   <string name="level">等级%d</string> | ||||
|   <string name="profileLevel">%s(%s級)</string> | ||||
|   <string name="images_uploaded">已上传图片</string> | ||||
|   <string name="image_reverts">未还原图片</string> | ||||
|   <string name="images_used_by_wiki">使用过的图片</string> | ||||
|  | @ -448,6 +453,7 @@ | |||
|   <string name="map_application_missing">您的设备上无法找到兼容的地图应用程序。请安装地图应用程序来使用此功能。</string> | ||||
|   <string name="title_page_bookmarks_pictures">图片</string> | ||||
|   <string name="title_page_bookmarks_locations">方位</string> | ||||
|   <string name="title_page_bookmarks_categories">分类</string> | ||||
|   <string name="menu_bookmark">添加/删除书签</string> | ||||
|   <string name="provider_bookmarks">书签</string> | ||||
|   <string name="bookmark_empty">您还未加入任何书签</string> | ||||
|  | @ -850,4 +856,8 @@ | |||
|   <string name="red_pin">这个地点还没有照片,快去拍一张吧!</string> | ||||
|   <string name="green_pin">这个地点已经有照片了。</string> | ||||
|   <string name="grey_pin">现在检查这个地点是否有照片。</string> | ||||
|   <string name="error_while_loading">加载时出错</string> | ||||
|   <string name="usages_on_commons_heading">维基共享资源</string> | ||||
|   <string name="usages_on_other_wikis_heading">其它wiki</string> | ||||
|   <string name="file_usages_container_heading">文件用途</string> | ||||
| </resources> | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
|     <dimen name="gigantic_gap">64dp</dimen> | ||||
|     <dimen name="standard_gap">16dp</dimen> | ||||
|     <dimen name="small_gap">8dp</dimen> | ||||
|     <dimen name="login_gap">20dp</dimen> | ||||
|     <dimen name="small_height">7dp</dimen> | ||||
|     <dimen name="tiny_gap">4dp</dimen> | ||||
|     <dimen name="very_tiny_gap">2dp</dimen> | ||||
|  |  | |||
|  | @ -860,5 +860,7 @@ Upload your first media by tapping on the add button.</string> | |||
|   <string name="usages_on_other_wikis_heading">Other wikis</string> | ||||
|   <string name="bullet_point">•</string> | ||||
|   <string name="file_usages_container_heading">File usages</string> | ||||
|   <string name="caption">Caption</string> | ||||
|   <string name="caption_copied_to_clipboard">Caption copied to clipboard</string> | ||||
| 
 | ||||
| </resources> | ||||
|  |  | |||
|  | @ -306,7 +306,7 @@ class NearbyParentFragmentPresenterTest { | |||
|         // 111.19 km real distance, return false if 148306.444306 >  currentLocationSearchRadius | ||||
|         NearbyController.currentLocationSearchRadius = 148306.0 | ||||
|         val isClose = nearbyPresenter.searchCloseToCurrentLocation() | ||||
|         assertFalse(isClose!!.equals(false)) | ||||
|         assertFalse(!isClose) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -318,7 +318,7 @@ class NearbyParentFragmentPresenterTest { | |||
|         // 111.19 km real distance, return false if 148253.333 >  currentLocationSearchRadius | ||||
|         NearbyController.currentLocationSearchRadius = 148307.0 | ||||
|         val isClose = nearbyPresenter.searchCloseToCurrentLocation() | ||||
|         assertTrue(isClose!!) | ||||
|         assertTrue(isClose) | ||||
|     } | ||||
| 
 | ||||
|     fun expectMapAndListUpdate() { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nicolas Raoul
						Nicolas Raoul