diff --git a/app/build.gradle b/app/build.gradle
index 14bf5f3b7..f50a4e6dd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -50,6 +50,8 @@ dependencies {
implementation "com.google.android.material:material:1.12.0"
implementation 'com.karumi:dexter:5.0.0'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+ implementation 'androidx.compose.ui:ui-tooling-preview'
+ androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
// Jetpack Compose
def composeBom = platform('androidx.compose:compose-bom:2024.11.00')
@@ -87,6 +89,8 @@ dependencies {
// Dependency injector
implementation "com.google.dagger:dagger-android:$DAGGER_VERSION"
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
+ debugImplementation 'androidx.compose.ui:ui-tooling'
+ debugImplementation 'androidx.compose.ui:ui-test-manifest'
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
annotationProcessor "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ab2edf719..e7c64f929 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -55,6 +55,10 @@
android:theme="@style/LightAppTheme"
tools:ignore="GoogleAppIndexingWarning"
tools:replace="android:appComponentFactory">
+
diff --git a/app/src/main/java/fr/free/nrw/commons/activity/SingleWebViewActivity.kt b/app/src/main/java/fr/free/nrw/commons/activity/SingleWebViewActivity.kt
new file mode 100644
index 000000000..284c84caf
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/activity/SingleWebViewActivity.kt
@@ -0,0 +1,181 @@
+package fr.free.nrw.commons.activity
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.webkit.ConsoleMessage
+import android.webkit.WebChromeClient
+import android.webkit.WebResourceRequest
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import fr.free.nrw.commons.R
+import timber.log.Timber
+
+/**
+ * SingleWebViewActivity is a reusable activity webView based on a given url(initial url) and
+ * closes itself when a specified success URL is reached to success url.
+ */
+class SingleWebViewActivity : ComponentActivity() {
+ @OptIn(ExperimentalMaterial3Api::class)
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val url = intent.getStringExtra(VANISH_ACCOUNT_URL)
+ val successUrl = intent.getStringExtra(VANISH_ACCOUNT_SUCCESS_URL)
+ if (url == null || successUrl == null) {
+ finish()
+ return
+ }
+ enableEdgeToEdge()
+ setContent {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ modifier = Modifier,
+ title = { Text(getString(R.string.vanish_account)) },
+ navigationIcon = {
+ IconButton(
+ onClick = {
+ // Close the WebView Activity if the user taps the back button
+ finish()
+ },
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ // TODO("Add contentDescription)
+ contentDescription = ""
+ )
+ }
+ }
+ )
+ },
+ content = {
+ WebViewComponent(
+ url = url,
+ successUrl = successUrl,
+ onSuccess = {
+ // TODO Redirect the user to login screen like we do when the user logout's
+ finish()
+ },
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it)
+ )
+ }
+ )
+ }
+ }
+
+
+ /**
+ * @param url The initial URL which we are loading in the WebView.
+ * @param successUrl The URL that, when reached, triggers the `onSuccess` callback.
+ * @param onSuccess A callback that is invoked when the current url of webView is successUrl.
+ * This is used when we want to close when the webView once a success url is hit.
+ * @param modifier An optional [Modifier] to customize the layout or appearance of the WebView.
+ */
+ @SuppressLint("SetJavaScriptEnabled")
+ @Composable
+ private fun WebViewComponent(
+ url: String,
+ successUrl: String,
+ onSuccess: () -> Unit,
+ modifier: Modifier = Modifier
+ ) {
+ val webView = remember { mutableStateOf(null) }
+ AndroidView(
+ modifier = modifier,
+ factory = {
+ WebView(it).apply {
+ settings.apply {
+ javaScriptEnabled = true
+ domStorageEnabled = true
+ javaScriptCanOpenWindowsAutomatically = true
+
+ }
+ webViewClient = object : WebViewClient() {
+ override fun shouldOverrideUrlLoading(
+ view: WebView?,
+ request: WebResourceRequest?
+ ): Boolean {
+
+ request?.url?.let { url ->
+ Timber.d("URL Loading: $url")
+ if (url.toString() == successUrl) {
+ Timber.d("Success URL detected. Closing WebView.")
+ onSuccess() // Close the activity
+ return true
+ }
+ return false
+ }
+ return false
+ }
+
+ override fun onPageFinished(view: WebView?, url: String?) {
+ super.onPageFinished(view, url)
+ }
+
+ }
+
+ webChromeClient = object : WebChromeClient() {
+ override fun onConsoleMessage(message: ConsoleMessage): Boolean {
+ Timber.d("Console: ${message.message()} -- From line ${message.lineNumber()} of ${message.sourceId()}")
+ return true
+ }
+ }
+
+ loadUrl(url)
+ }
+ },
+ update = {
+ webView.value = it
+ }
+ )
+
+ }
+
+ companion object {
+ private const val VANISH_ACCOUNT_URL = "VanishAccountUrl"
+ private const val VANISH_ACCOUNT_SUCCESS_URL = "vanishAccountSuccessUrl"
+
+ /**
+ * Launch the WebViewActivity with the specified URL and success URL.
+ * @param context The context from which the activity is launched.
+ * @param url The initial URL to load in the WebView.
+ * @param successUrl The URL that triggers the WebView to close when matched.
+ */
+ fun showWebView(
+ context: Context,
+ url: String,
+ successUrl: String
+ ) {
+ val intent = Intent(
+ context,
+ SingleWebViewActivity::class.java
+ ).apply {
+ putExtra(VANISH_ACCOUNT_URL, url)
+ putExtra(VANISH_ACCOUNT_SUCCESS_URL, successUrl)
+ }
+ context.startActivity(intent)
+ }
+ }
+}
+
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt
index 47ee8588b..75c4ac26d 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt
+++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt
@@ -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"
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.java b/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.java
deleted file mode 100644
index c1c8fac18..000000000
--- a/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.java
+++ /dev/null
@@ -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();
- }
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.kt b/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.kt
new file mode 100644
index 000000000..378a98893
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/concurrency/BackgroundPoolExceptionHandler.kt
@@ -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()
+ }
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.java b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.java
deleted file mode 100644
index 80931b1c1..000000000
--- a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.java
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.kt b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.kt
new file mode 100644
index 000000000..0efe057f2
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionAwareThreadPoolExecutor.kt
@@ -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)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.java b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.java
deleted file mode 100644
index 38690305a..000000000
--- a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package fr.free.nrw.commons.concurrency;
-
-import androidx.annotation.NonNull;
-
-public interface ExceptionHandler {
- void onException(@NonNull Throwable t);
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.kt b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.kt
new file mode 100644
index 000000000..6b3d2a0f7
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/concurrency/ExceptionHandler.kt
@@ -0,0 +1,7 @@
+package fr.free.nrw.commons.concurrency
+
+interface ExceptionHandler {
+
+ fun onException(t: Throwable)
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.java b/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.java
deleted file mode 100644
index f057f61b2..000000000
--- a/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.java
+++ /dev/null
@@ -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 ScheduledFuture schedule(Callable 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);
- }
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.kt b/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.kt
new file mode 100644
index 000000000..46138d676
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/concurrency/ThreadPoolService.kt
@@ -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 schedule(callable: Callable, time: Long, timeUnit: TimeUnit): ScheduledFuture {
+ 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)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt
index 84ce0eb9c..92fab56af 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.kt
@@ -101,7 +101,7 @@ data class Contribution constructor(
*/
fun formatCaptions(uploadMediaDetails: List) =
uploadMediaDetails
- .associate { it.languageCode!! to it.captionText }
+ .associate { it.languageCode!! to it.captionText!! }
.filter { it.value.isNotBlank() }
/**
@@ -112,7 +112,7 @@ data class Contribution constructor(
*/
fun formatDescriptions(descriptions: List) =
descriptions
- .filter { it.descriptionText.isNotEmpty() }
+ .filter { !it.descriptionText.isNullOrEmpty() }
.joinToString(separator = "") { "{{${it.languageCode}|1=${it.descriptionText}}}" }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/coordinates/CoordinateEditHelper.kt b/app/src/main/java/fr/free/nrw/commons/coordinates/CoordinateEditHelper.kt
index 3095497c3..1bad2e2a5 100644
--- a/app/src/main/java/fr/free/nrw/commons/coordinates/CoordinateEditHelper.kt
+++ b/app/src/main/java/fr/free/nrw/commons/coordinates/CoordinateEditHelper.kt
@@ -61,16 +61,16 @@ class CoordinateEditHelper @Inject constructor(
/**
* Replaces new coordinates
* @param media to be added
- * @param Latitude to be added
- * @param Longitude to be added
- * @param Accuracy to be added
+ * @param latitude to be added
+ * @param longitude to be added
+ * @param accuracy to be added
* @return Observable
*/
private fun addCoordinates(
media: Media,
- Latitude: String,
- Longitude: String,
- Accuracy: String
+ latitude: String,
+ longitude: String,
+ accuracy: String
): Observable? {
Timber.d("thread is coordinates adding %s", Thread.currentThread().getName())
val summary = "Adding Coordinates"
@@ -83,9 +83,9 @@ class CoordinateEditHelper @Inject constructor(
.blockingGet()
}
- if (Latitude != null) {
- buffer.append("\n{{Location|").append(Latitude).append("|").append(Longitude)
- .append("|").append(Accuracy).append("}}")
+ if (latitude != null) {
+ buffer.append("\n{{Location|").append(latitude).append("|").append(longitude)
+ .append("|").append(accuracy).append("}}")
}
val editedLocation = buffer.toString()
@@ -141,7 +141,7 @@ class CoordinateEditHelper @Inject constructor(
* @param media to be added
* @param latitude to be added
* @param longitude to be added
- * @param Accuracy to be added
+ * @param accuracy to be added
* @param result to be added
* @return boolean
*/
@@ -150,7 +150,7 @@ class CoordinateEditHelper @Inject constructor(
media: Media,
latitude: String,
longitude: String,
- Accuracy: String,
+ accuracy: String,
result: Boolean
): Boolean {
val message: String
@@ -160,7 +160,7 @@ class CoordinateEditHelper @Inject constructor(
media.coordinates = fr.free.nrw.commons.location.LatLng(
latitude.toDouble(),
longitude.toDouble(),
- Accuracy.toFloat()
+ accuracy.toFloat()
)
title += ": " + context.getString(R.string.coordinates_edit_helper_show_edit_title_success)
val coordinatesInMessage = StringBuilder()
diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt
index 52b615175..4e2d58bab 100644
--- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt
+++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt
@@ -271,7 +271,7 @@ class CustomSelectorActivity :
dialog.setCancelable(false)
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(R.layout.custom_selector_info_dialog)
- (dialog.findViewById(R.id.btn_ok) as Button).setOnClickListener { dialog.dismiss() }
+ (dialog.findViewById