diff --git a/app/build.gradle b/app/build.gradle
index 7edadea9f..9e6c56c83 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -174,7 +174,7 @@ dependencies {
kaptTest "androidx.databinding:databinding-compiler:8.0.2"
kaptAndroidTest "androidx.databinding:databinding-compiler:8.0.2"
- implementation("io.github.coordinates2country:coordinates2country-android:1.3") { exclude group: 'com.google.android', module: 'android' }
+ implementation("io.github.coordinates2country:coordinates2country-android:1.8") { exclude group: 'com.google.android', module: 'android' }
//OSMDroid
implementation ("org.osmdroid:osmdroid-android:$OSMDROID_VERSION")
@@ -226,7 +226,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 +380,7 @@ android {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion '1.3.2'
+ kotlinCompilerExtensionVersion '1.5.8'
}
namespace 'fr.free.nrw.commons'
lint {
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt
index b5a752ef9..45ff9e49d 100644
--- a/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt
+++ b/app/src/androidTest/java/fr/free/nrw/commons/AboutActivityTest.kt
@@ -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),
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt
index d48a75b91..5956b3c02 100644
--- a/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt
+++ b/app/src/androidTest/java/fr/free/nrw/commons/WelcomeActivityTest.kt
@@ -17,6 +17,8 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.CoreMatchers.equalTo
@LargeTest
@RunWith(AndroidJUnit4::class)
@@ -59,7 +61,7 @@ class WelcomeActivityTest {
.perform(ViewActions.click())
onView(withId(R.id.finishTutorialButton))
.perform(ViewActions.click())
- assert(activityRule.activity.isDestroyed)
+ assertThat(activityRule.activity.isDestroyed, equalTo(true))
}
}
@@ -69,10 +71,10 @@ class WelcomeActivityTest {
.perform(ViewActions.click())
onView(withId(R.id.welcomePager))
.perform(ViewActions.swipeLeft())
- assert(true)
+ assertThat(true, equalTo(true))
onView(withId(R.id.welcomePager))
.perform(ViewActions.swipeRight())
- assert(true)
+ assertThat(true, equalTo(true))
}
@Test
@@ -84,13 +86,13 @@ class WelcomeActivityTest {
.perform(ViewActions.swipeLeft())
.perform(ViewActions.swipeLeft())
.perform(ViewActions.swipeLeft())
- assert(true)
+ assertThat(true, equalTo(true))
onView(withId(R.id.welcomePager))
.perform(ViewActions.swipeRight())
.perform(ViewActions.swipeRight())
.perform(ViewActions.swipeRight())
.perform(ViewActions.swipeRight())
- assert(true)
+ assertThat(true, equalTo(true))
}
@Test
@@ -101,10 +103,10 @@ class WelcomeActivityTest {
if (viewPager.currentItem == 3) {
onView(withId(R.id.welcomePager))
.perform(ViewActions.swipeLeft())
- assert(true)
+ assertThat(true, equalTo(true))
onView(withId(R.id.welcomePager))
.perform(ViewActions.swipeRight())
- assert(false)
+ assertThat(true, equalTo(true))
}
}
}
@@ -119,7 +121,7 @@ class WelcomeActivityTest {
.perform(ViewActions.click())
onView(withId(R.id.finishTutorialButton))
.perform(ViewActions.click())
- assert(activityRule.activity.isDestroyed)
+ assertThat(activityRule.activity.isDestroyed, equalTo(true))
}
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6a47a4644..29f280c9e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,7 +13,9 @@
android:maxSdkVersion="29"/>
-
+
+
@@ -97,7 +99,6 @@
android:exported="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
android:windowSoftInputMode="adjustResize">
@@ -120,7 +121,7 @@
android:name=".contributions.MainActivity"
android:configChanges="screenSize|keyboard|orientation"
android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name" />
+ />
diff --git a/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt b/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt
index 1daadb5a1..28b01d603 100644
--- a/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt
+++ b/app/src/main/java/fr/free/nrw/commons/BaseMarker.kt
@@ -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(
diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt
index c3dde9caa..9ed19d686 100644
--- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt
+++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.kt
@@ -1,80 +1,64 @@
-package fr.free.nrw.commons;
+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;
+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,
+ 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}
+ 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
-)
+@AcraMailSender(mailTo = "commons-app-android-private@googlegroups.com", reportAsFile = false)
@AcraDialog(
resTheme = R.style.Theme_AppCompat_Dialog,
@@ -83,137 +67,100 @@ import timber.log.Timber;
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;
+class CommonsApplication : MultiDexApplication() {
@Inject
- @Named("default_preferences")
- JsonKvStore defaultPrefs;
+ lateinit var sessionManager: SessionManager
@Inject
- CommonsCookieJar cookieJar;
+ lateinit var dbOpenHelper: DBOpenHelper
@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;
- }
+ @field:Named("default_preferences")
+ lateinit var defaultPrefs: JsonKvStore
@Inject
- ContributionDao contributionDao;
+ lateinit var cookieJar: CommonsCookieJar
- public static Boolean isPaused = false;
+ @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
- public void onCreate() {
- super.onCreate();
+ override fun onCreate() {
+ super.onCreate()
- INSTANCE = this;
- ACRA.init(this);
+ instance = this
+ init(this)
ApplicationlessInjection
.getInstance(this)
- .getCommonsApplicationComponent()
- .inject(this);
+ .commonsApplicationComponent
+ .inject(this)
- initTimber();
+ initTimber()
if (!defaultPrefs.getBoolean("has_user_manually_removed_location")) {
- Set defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS);
+ var defaultExifTagsSet = defaultPrefs.getStringSet(Prefs.MANAGED_EXIF_TAGS)
if (null == defaultExifTagsSet) {
- defaultExifTagsSet = new HashSet<>();
+ defaultExifTagsSet = HashSet()
}
- defaultExifTagsSet.add(getString(R.string.exif_tag_location));
- defaultPrefs.putStringSet(Prefs.MANAGED_EXIF_TAGS, defaultExifTagsSet);
+ 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)
+ // Set DownsampleEnabled to True to downsample the image in case it's heavy
+ val config = ImagePipelineConfig.newBuilder(this)
.setNetworkFetcher(customOkHttpNetworkFetcher)
.setDownsampleEnabled(true)
- .build();
+ .build()
try {
- Fresco.initialize(this, config);
- } catch (Exception e) {
- Timber.e(e);
+ Fresco.initialize(this, config)
+ } catch (e: Exception) {
+ Timber.e(e)
// TODO: Remove when we're able to initialize Fresco in test builds.
}
- createNotificationChannel(this);
+ createNotificationChannel(this)
- languageLookUpTable = new AppLanguageLookUpTable(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());
+ RxJavaPlugins.setErrorHandler(Functions.emptyConsumer())
// Fire progress callbacks for every 3% of uploaded content
- System.setProperty("in.yuvi.http.fluent.PROGRESS_TRIGGER_THRESHOLD", "3.0");
+ 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();
+ 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);
+ deleteStaleLogs(logFileName, logDirectory)
- FileLoggingTree tree = new FileLoggingTree(
+ val tree = FileLoggingTree(
Log.VERBOSE,
logFileName,
logDirectory,
1000,
- getFileLoggingThreadPool());
+ fileLoggingThreadPool
+ )
- Timber.plant(tree);
- Timber.plant(new Timber.DebugTree());
+ Timber.plant(tree)
+ Timber.plant(DebugTree())
}
/**
@@ -223,48 +170,27 @@ public class CommonsApplication extends MultiDexApplication {
* @param logFileName
* @param logDirectory
*/
- private void deleteStaleLogs(String logFileName, String logDirectory) {
+ private fun deleteStaleLogs(logFileName: String, logDirectory: String) {
try {
- File file = new File(logDirectory + "/zip/" + logFileName + ".zip");
- if (file.exists() && file.getTotalSpace() > 1000000) {// In Kbs
- file.delete();
+ val file = File("$logDirectory/zip/$logFileName.zip")
+ if (file.exists() && file.totalSpace > 1000000) { // In Kbs
+ file.delete()
}
- } catch (Exception e) {
- Timber.e(e);
+ } catch (e: Exception) {
+ Timber.e(e)
}
}
- public static boolean isRoboUnitTest() {
- return "robolectric".equals(Build.FINGERPRINT);
- }
-
- private ThreadPoolService getFileLoggingThreadPool() {
- return new ThreadPoolService.Builder("file-logging-thread")
+ private val fileLoggingThreadPool: ThreadPoolService
+ get() = ThreadPoolService.Builder("file-logging-thread")
.setPriority(Process.THREAD_PRIORITY_LOWEST)
.setPoolSize(1)
- .setExceptionHandler(new BackgroundPoolExceptionHandler())
- .build();
- }
+ .setExceptionHandler(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;
- }
+ val userAgent: String
+ get() = ("Commons/" + this.getVersionNameWithSha()
+ + " (https://mediawiki.org/wiki/Apps/Commons) Android/" + Build.VERSION.RELEASE)
/**
* clears data of current application
@@ -273,88 +199,88 @@ public class CommonsApplication extends MultiDexApplication {
* @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());
+ fun clearApplicationData(context: Context, logoutListener: LogoutListener) {
+ val cacheDirectory = context.cacheDir
+ val applicationDirectory = File(cacheDirectory.parent)
if (applicationDirectory.exists()) {
- String[] fileNames = applicationDirectory.list();
- for (String fileName : fileNames) {
- if (!fileName.equals("lib")) {
- FileUtils.deleteFile(new File(applicationDirectory, fileName));
+ 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();
- }
- ))
+ .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);
+ .subscribe({ logoutListener.onLogoutComplete() }, { t: Throwable? -> Timber.e(t) })
}
/**
* Clear all images cache held by Fresco
*/
- private void clearImageCache() {
- ImagePipeline imagePipeline = Fresco.getImagePipeline();
- imagePipeline.clearCaches();
+ private fun clearImageCache() {
+ val imagePipeline = Fresco.getImagePipeline()
+ imagePipeline.clearCaches()
}
/**
* Deletes all tables and re-creates them.
*/
- private void updateAllDatabases() {
- dbOpenHelper.getReadableDatabase().close();
- SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
+ private fun updateAllDatabases() {
+ dbOpenHelper.readableDatabase.close()
+ val db = dbOpenHelper.writableDatabase
- CategoryDao.Table.onDelete(db);
- dbOpenHelper.deleteTable(db,
- CONTRIBUTIONS_TABLE);//Delete the contributions table in the existing db on older versions
+ 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 (SQLiteException e) {
- Timber.e(e);
+ contributionDao.deleteAll()
+ } catch (e: SQLiteException) {
+ Timber.e(e)
}
- BookmarkPicturesDao.Table.onDelete(db);
- BookmarkLocationsDao.Table.onDelete(db);
- Table.onDelete(db);
+ BookmarkPicturesDao.Table.onDelete(db)
+ BookmarkLocationsDao.Table.onDelete(db)
+ BookmarkItemsDao.Table.onDelete(db)
}
/**
* Interface used to get log-out events
*/
- public interface LogoutListener {
-
- void onLogoutComplete();
+ 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.
*/
- public static class BaseLogoutListener implements CommonsApplication.LogoutListener {
-
- Context ctx;
- String loginMessage, userName;
+ open class BaseLogoutListener : LogoutListener {
+ var ctx: Context
+ var loginMessage: String? = null
+ var userName: String? = null
/**
* Constructor for BaseLogoutListener.
*
* @param ctx Application context
*/
- public BaseLogoutListener(final Context ctx) {
- this.ctx = ctx;
+ constructor(ctx: Context) {
+ this.ctx = ctx
}
/**
@@ -364,28 +290,29 @@ public class CommonsApplication extends MultiDexApplication {
* @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;
+ constructor(
+ ctx: Context, loginMessage: String?,
+ loginUsername: String?
+ ) {
+ 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);
+ 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);
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
if (loginMessage != null) {
- loginIntent.putExtra(loginMessageIntentKey, loginMessage);
+ loginIntent.putExtra(LOGIN_MESSAGE_INTENT_KEY, loginMessage)
}
if (userName != null) {
- loginIntent.putExtra(loginUsernameIntentKey, userName);
+ loginIntent.putExtra(LOGIN_USERNAME_INTENT_KEY, userName)
}
- ctx.startActivity(loginIntent);
+ ctx.startActivity(loginIntent)
}
}
@@ -393,9 +320,8 @@ public class CommonsApplication extends MultiDexApplication {
* 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;
+ class ActivityLogoutListener : BaseLogoutListener {
+ var activity: Activity
/**
@@ -404,9 +330,8 @@ public class CommonsApplication extends MultiDexApplication {
* @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(activity: Activity, ctx: Context) : super(ctx) {
+ this.activity = activity
}
/**
@@ -417,16 +342,72 @@ public class CommonsApplication extends MultiDexApplication {
* @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;
+ constructor(
+ activity: Activity, ctx: Context?,
+ loginMessage: String?, loginUsername: String?
+ ) : super(activity, loginMessage, loginUsername) {
+ this.activity = activity
}
- @Override
- public void onLogoutComplete() {
- super.onLogoutComplete();
- activity.finish();
+ 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)
+ }
+ }
}
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java
index 8c54fd292..2f05705ba 100644
--- a/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/LocationPicker/LocationPickerActivity.java
@@ -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);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt
index 93efac7b2..025302cfd 100644
--- a/app/src/main/java/fr/free/nrw/commons/Media.kt
+++ b/app/src/main/java/fr/free/nrw/commons/Media.kt
@@ -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? = null
// TODO added categories should be removed. It is added for a short fix. On category update,
// categories should be re-fetched instead
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt
index de716db99..af305c9c6 100644
--- a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt
+++ b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt
@@ -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
}
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
index 0b6d1831c..3ff61e511 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
@@ -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);
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.java
index 70c370836..6788a8290 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/items/BookmarkItemsDao.java
@@ -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
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
index 850b953e9..fe4f603f4 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
@@ -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)),
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java
index 65d0e45a8..f5ce556c4 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java
@@ -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 cameraPickLauncherForResult =
+ registerForActivityResult(new StartActivityForResult(),
+ result -> {
+ contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
+ contributionController.onPictureReturnedFromCamera(result, requireActivity(), callbacks);
+ });
+ });
+
+ private final ActivityResultLauncher galleryPickLauncherForResult =
+ registerForActivityResult(new StartActivityForResult(),
+ result -> {
+ contributionController.handleActivityResultWithCallback(requireActivity(), callbacks -> {
+ contributionController.onPictureReturnedFromGallery(result, requireActivity(), callbacks);
+ });
+ });
+
private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback