diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index a5d456928e..f39734eb4b 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -1,16 +1,12 @@
-
-
-
-
@@ -25,13 +21,11 @@
-
-
@@ -47,6 +41,5 @@
-
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index a9c0c498cf..bb3309714d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,6 +16,11 @@ if (isRunningOnTravisAndIsNotPRBuild) {
dependencies {
+ def appcompat_version = "1.7.0"
+ implementation "androidx.appcompat:appcompat:$appcompat_version"
+ // For loading and tinting drawables on older versions of the platform
+ implementation "androidx.appcompat:appcompat-resources:$appcompat_version"
+
// Utils
implementation 'in.yuvi:http.fluent:1.3'
implementation 'com.google.code.gson:gson:2.8.5'
@@ -380,13 +385,16 @@ android {
compose true
}
composeOptions {
- kotlinCompilerExtensionVersion '1.3.2'
+ kotlinCompilerExtensionVersion '1.5.8'
}
namespace 'fr.free.nrw.commons'
lint {
abortOnError false
disable 'MissingTranslation', 'ExtraTranslation'
}
+ androidResources {
+ generateLocaleConfig true
+ }
}
String getTestUserName() {
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/ui/AppLanguagesSystemTest.java b/app/src/androidTest/java/fr/free/nrw/commons/ui/AppLanguagesSystemTest.java
new file mode 100644
index 0000000000..3abe28e365
--- /dev/null
+++ b/app/src/androidTest/java/fr/free/nrw/commons/ui/AppLanguagesSystemTest.java
@@ -0,0 +1,58 @@
+package fr.free.nrw.commons.ui;
+
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.platform.app.InstrumentationRegistry;
+import android.content.Intent;
+import android.provider.Settings;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class AppLanguagesSystemTest {
+
+ private UiDevice device;
+
+ @Before
+ public void setUp() {
+ // Initiate UI Automator
+ device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // Use Intent to start system application
+ Intent intent = new Intent(Settings.ACTION_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ InstrumentationRegistry.getInstrumentation().getContext().startActivity(intent);
+
+ // wait Settings starting
+ device.waitForIdle();
+ }
+
+ @Test
+ public void testCommonsAppLanguageOptionExists() throws Exception {
+ // 1. Find and click "System"
+ UiScrollable settingsList = new UiScrollable(new UiSelector().scrollable(true));
+ UiObject systemOption = settingsList.getChildByText(new UiSelector().text("System"), "System");
+ systemOption.click();
+
+ // 2. scroll and find "Languages & input"
+ UiObject languagesInputOption = device.findObject(new UiSelector()
+ .className("android.widget.TextView")
+ .textContains("Gboard"));
+ languagesInputOption.clickAndWaitForNewWindow();
+
+
+
+
+ // 3. detect "App languages" and click
+ UiObject appLanguagesOption = device.findObject(new UiSelector().text("App languages"));
+ appLanguagesOption.clickAndWaitForNewWindow();
+
+ // 4. detect "Commons" APP is there
+ UiScrollable appLanguagesList = new UiScrollable(new UiSelector().scrollable(true));
+ UiObject commonsAppOption = appLanguagesList.getChildByText(new UiSelector().text("Commons"), "Commons");
+
+ assert(commonsAppOption.exists());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6a47a46447..ee7cebde26 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -193,6 +193,13 @@
+
+
+
{
onSupportNavigateUp();
@@ -485,13 +485,13 @@ public enum ActiveFragment {
/**
* Load default language in onCreate from SharedPreferences
*/
- private void loadLocale() {
- final SharedPreferences preferences = getSharedPreferences("Settings",
- Activity.MODE_PRIVATE);
- final String language = preferences.getString("language", "");
- final SettingsFragment settingsFragment = new SettingsFragment();
- settingsFragment.setLocale(this, language);
- }
+// private void loadLocale() {
+// final SharedPreferences preferences = getSharedPreferences("Settings",
+// Activity.MODE_PRIVATE);
+// final String language = preferences.getString("language", "");
+// final SettingsFragment settingsFragment = new SettingsFragment();
+// settingsFragment.setLocale(this, language);
+// }
public NavTabLayout.OnNavigationItemSelectedListener getNavListener() {
return navListener;
diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt
index b75a6e1d4f..872388f40d 100644
--- a/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt
+++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/NotForUploadStatusDao.kt
@@ -15,19 +15,19 @@ abstract class NotForUploadStatusDao {
* Insert into Not For Upload status.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract suspend fun insert(notForUploadStatus: NotForUploadStatus)
+ abstract fun insert(notForUploadStatus: NotForUploadStatus)
/**
* Delete Not For Upload status entry.
*/
@Delete
- abstract suspend fun delete(notForUploadStatus: NotForUploadStatus)
+ abstract fun delete(notForUploadStatus: NotForUploadStatus)
/**
* Query Not For Upload status with image sha1.
*/
@Query("SELECT * FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
- abstract suspend fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus?
+ abstract fun getFromImageSHA1(imageSHA1: String): NotForUploadStatus?
/**
* Asynchronous image sha1 query.
@@ -38,7 +38,7 @@ abstract class NotForUploadStatusDao {
* Deletion Not For Upload status with image sha1.
*/
@Query("DELETE FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
- abstract suspend fun deleteWithImageSHA1(imageSHA1: String)
+ abstract fun deleteWithImageSHA1(imageSHA1: String)
/**
* Asynchronous image sha1 deletion.
@@ -49,5 +49,5 @@ abstract class NotForUploadStatusDao {
* Check whether the imageSHA1 is present in database
*/
@Query("SELECT COUNT() FROM images_not_for_upload_table WHERE imageSHA1 = (:imageSHA1) ")
- abstract suspend fun find(imageSHA1: String): Int
+ abstract fun find(imageSHA1: String): Int
}
diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt
index 378af5b8db..03cbb176fe 100644
--- a/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt
+++ b/app/src/main/java/fr/free/nrw/commons/customselector/database/UploadedStatusDao.kt
@@ -17,31 +17,31 @@ abstract class UploadedStatusDao {
* Insert into uploaded status.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract suspend fun insert(uploadedStatus: UploadedStatus)
+ abstract fun insert(uploadedStatus: UploadedStatus)
/**
* Update uploaded status entry.
*/
@Update
- abstract suspend fun update(uploadedStatus: UploadedStatus)
+ abstract fun update(uploadedStatus: UploadedStatus)
/**
* Delete uploaded status entry.
*/
@Delete
- abstract suspend fun delete(uploadedStatus: UploadedStatus)
+ abstract fun delete(uploadedStatus: UploadedStatus)
/**
* Query uploaded status with image sha1.
*/
@Query("SELECT * FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) ")
- abstract suspend fun getFromImageSHA1(imageSHA1: String): UploadedStatus?
+ abstract fun getFromImageSHA1(imageSHA1: String): UploadedStatus?
/**
* Query uploaded status with modified image sha1.
*/
@Query("SELECT * FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) ")
- abstract suspend fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus?
+ abstract fun getFromModifiedImageSHA1(modifiedImageSHA1: String): UploadedStatus?
/**
* Asynchronous insert into uploaded status table.
@@ -55,7 +55,7 @@ abstract class UploadedStatusDao {
* Check whether the imageSHA1 is present in database
*/
@Query("SELECT COUNT() FROM uploaded_table WHERE imageSHA1 = (:imageSHA1) AND imageResult = (:imageResult) ")
- abstract suspend fun findByImageSHA1(
+ abstract fun findByImageSHA1(
imageSHA1: String,
imageResult: Boolean,
): Int
@@ -66,7 +66,7 @@ abstract class UploadedStatusDao {
@Query(
"SELECT COUNT() FROM uploaded_table WHERE modifiedImageSHA1 = (:modifiedImageSHA1) AND modifiedImageResult = (:modifiedImageResult) ",
)
- abstract suspend fun findByModifiedImageSHA1(
+ abstract fun findByModifiedImageSHA1(
modifiedImageSHA1: String,
modifiedImageResult: Boolean,
): Int
diff --git a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt
index 81ef5533d9..b71670e2a5 100644
--- a/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt
+++ b/app/src/main/java/fr/free/nrw/commons/recentlanguages/RecentLanguagesAdapter.kt
@@ -27,7 +27,8 @@ class RecentLanguagesAdapter constructor(
override fun isEnabled(position: Int) =
recentLanguages[position].languageCode.let {
- it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
+// it.isNotEmpty() && !it.contains(selectedLanguages.values.first())
+ true
}
override fun getCount() = recentLanguages.size
diff --git a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
index 3342143479..d0451e24ca 100644
--- a/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
+++ b/app/src/main/java/fr/free/nrw/commons/settings/Prefs.java
@@ -8,6 +8,8 @@ public class Prefs {
public static final String UPLOADS_SHOWING = "uploadsshowing";
public static final String MANAGED_EXIF_TAGS = "managed_exif_tags";
public static final String DESCRIPTION_LANGUAGE = "languageDescription";
+ public static final String SECONDARY_LANGUAGE = "languageSecondary";
+
public static final String APP_UI_LANGUAGE = "appUiLanguage";
public static final String KEY_THEME_VALUE = "appThemePref";
diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
index 94e799aa29..971e7ab491 100644
--- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
@@ -3,6 +3,7 @@
import static android.content.Context.MODE_PRIVATE;
import android.Manifest.permission;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
@@ -21,6 +22,8 @@
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.core.os.LocaleListCompat;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
@@ -79,6 +82,7 @@ public class SettingsFragment extends PreferenceFragmentCompat {
private ListPreference themeListPreference;
private Preference descriptionLanguageListPreference;
+ private Preference descriptionSecondaryLanguageListPreference;
private Preference appUiLanguageListPreference;
private String keyLanguageListPreference;
private TextView recentLanguagesTextView;
@@ -152,6 +156,7 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
appUiLanguageListPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
+ System.out.println("Clicked appui");
prepareAppLanguages(appUiLanguageListPreference.getKey());
return true;
}
@@ -178,6 +183,28 @@ public boolean onPreferenceClick(Preference preference) {
}
});
+ descriptionSecondaryLanguageListPreference = findPreference("descriptionSecondaryLanguagePref");
+ assert descriptionSecondaryLanguageListPreference != null;
+ keyLanguageListPreference = descriptionSecondaryLanguageListPreference.getKey();
+ languageCode = getCurrentLanguageCode(keyLanguageListPreference);
+ assert languageCode != null;
+ if (languageCode.equals("")) {
+ // If current language code is empty, means none selected by user yet so use phone local
+ descriptionSecondaryLanguageListPreference.setSummary(Locale.getDefault().getDisplayLanguage());
+ } else {
+ // If any language is selected by user previously, use it
+ Locale defLocale = createLocale(languageCode);
+ descriptionSecondaryLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
+ }
+ descriptionSecondaryLanguageListPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ System.out.println("clickedseco");
+ prepareAppLanguages(descriptionSecondaryLanguageListPreference.getKey());
+ return true;
+ }
+ });
+
Preference betaTesterPreference = findPreference("becomeBetaTester");
betaTesterPreference.setOnPreferenceClickListener(preference -> {
Utils.handleWebUrl(getActivity(), Uri.parse(getResources().getString(R.string.beta_opt_in_link)));
@@ -205,6 +232,7 @@ public boolean onPreferenceClick(Preference preference) {
findPreference("useAuthorName").setEnabled(false);
findPreference("displayNearbyCardView").setEnabled(false);
findPreference("descriptionDefaultLanguagePref").setEnabled(false);
+ findPreference("descriptionSecondaryLanguagePref").setEnabled(false);
findPreference("displayLocationPermissionForCardView").setEnabled(false);
findPreference(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE).setEnabled(false);
findPreference("managed_exif_tags").setEnabled(false);
@@ -242,6 +270,7 @@ private void showLocationLossWarning() {
);
}
+ @SuppressLint("RestrictedApi")
@Override
protected Adapter onCreateAdapter(final PreferenceScreen preferenceScreen) {
return new PreferenceGroupAdapter(preferenceScreen) {
@@ -278,6 +307,8 @@ private void prepareTheme() {
*/
private void prepareAppLanguages(final String keyListPreference) {
+ System.out.println("gets to prepare app languages");
+
// Gets current language code from shared preferences
final String languageCode = getCurrentLanguageCode(keyListPreference);
final List recentLanguages = recentLanguagesDao.getRecentLanguages();
@@ -287,7 +318,7 @@ private void prepareAppLanguages(final String keyListPreference) {
assert languageCode != null;
if (languageCode.equals("")) {
- selectedLanguages.put(0, Locale.getDefault().getLanguage());
+ selectedLanguages.put(0, AppCompatDelegate.getApplicationLocales().toLanguageTags());
} else {
selectedLanguages.put(0, languageCode);
}
@@ -300,7 +331,16 @@ private void prepareAppLanguages(final String keyListPreference) {
} else {
selectedLanguages.put(0, languageCode);
}
+ } else if (keyListPreference.equals("descriptionSecondaryLanguagePref")) {
+
+ assert languageCode != null;
+ if (languageCode.equals("")) {
+ selectedLanguages.put(0, Locale.getDefault().getLanguage());
+
+ } else {
+ selectedLanguages.put(0, languageCode);
}
+ }
LanguagesAdapter languagesAdapter = new LanguagesAdapter(
getActivity(),
@@ -360,17 +400,17 @@ public void onItemClick(AdapterView> adapterView, View view, int i,
recentLanguagesDao.deleteRecentLanguage(languageCode);
}
recentLanguagesDao.addRecentLanguage(new Language(languageName, languageCode));
- saveLanguageValue(languageCode, keyListPreference);
Locale defLocale = createLocale(languageCode);
if(keyListPreference.equals("appUiDefaultLanguagePref")) {
appUiLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
- setLocale(requireActivity(), languageCode);
- getActivity().recreate();
- final Intent intent = new Intent(getActivity(), MainActivity.class);
- startActivity(intent);
- }else {
+ LocaleListCompat appLocale = LocaleListCompat.forLanguageTags(languageCode);
+ AppCompatDelegate.setApplicationLocales(appLocale);
+ }else if(keyListPreference.equals("descriptionDefaultLanguagePref")){
descriptionLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
}
+ else{
+ descriptionSecondaryLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
+ }
dialog.dismiss();
}
});
@@ -429,10 +469,8 @@ private void onRecentLanguageClicked(String keyListPreference, Dialog dialog, Ad
final Locale defLocale = createLocale(recentLanguageCode);
if (keyListPreference.equals("appUiDefaultLanguagePref")) {
appUiLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
- setLocale(requireActivity(), recentLanguageCode);
- getActivity().recreate();
- final Intent intent = new Intent(getActivity(), MainActivity.class);
- startActivity(intent);
+ LocaleListCompat appLocale = LocaleListCompat.forLanguageTags(recentLanguageCode);
+ AppCompatDelegate.setApplicationLocales(appLocale);
} else {
descriptionLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
}
@@ -496,6 +534,8 @@ private void saveLanguageValue(final String userSelectedValue, final String pref
defaultKvStore.putString(Prefs.APP_UI_LANGUAGE, userSelectedValue);
} else if (preferenceKey.equals("descriptionDefaultLanguagePref")) {
defaultKvStore.putString(Prefs.DESCRIPTION_LANGUAGE, userSelectedValue);
+ } else if (preferenceKey.equals("descriptionSecondaryLanguagePref")) {
+ defaultKvStore.putString(Prefs.SECONDARY_LANGUAGE, userSelectedValue);
}
}
@@ -511,6 +551,9 @@ private String getCurrentLanguageCode(final String preferenceKey) {
if (preferenceKey.equals("descriptionDefaultLanguagePref")) {
return defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, "");
}
+ if (preferenceKey.equals("descriptionSecondaryLanguagePref")) {
+ return defaultKvStore.getString(Prefs.SECONDARY_LANGUAGE, "");
+ }
return null;
}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt
index 2847fa0c0b..ebbe8452e7 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt
+++ b/app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt
@@ -51,7 +51,8 @@ class LanguagesAdapter constructor(
override fun isEnabled(position: Int) =
languageCodesList[position].let {
- it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
+// it.isNotEmpty() && !it.contains(selectedLanguages.values.first())
+ true
}
override fun getCount() = languageNamesList.size
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt
index f1e4917a0d..d20129475c 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt
+++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/BaseDelegateAdapter.kt
@@ -10,16 +10,14 @@ abstract class BaseDelegateAdapter(
areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new },
) : AsyncListDifferDelegationAdapter(
object : DiffUtil.ItemCallback() {
- override fun areItemsTheSame(
- oldItem: T,
- newItem: T,
- ) = areItemsTheSame(oldItem, newItem)
+ override fun areItemsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
+ return areItemsTheSame(oldItem, newItem)
+ }
- override fun areContentsTheSame(
- oldItem: T,
- newItem: T,
- ) = areContentsTheSame(oldItem, newItem)
- },
+ override fun areContentsTheSame(oldItem: T & Any, newItem: T & Any): Boolean {
+ return areContentsTheSame(oldItem, newItem)
+ }
+ },
*delegates,
) {
fun addAll(newResults: List) {
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt
index 6844003014..c20d65abf6 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt
+++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsDao.kt
@@ -22,16 +22,16 @@ abstract class DepictsDao {
private val maxItemsAllowed = 10
@Insert(onConflict = OnConflictStrategy.REPLACE)
- abstract suspend fun insert(depictedItem: Depicts)
+ abstract fun insert(depictedItem: Depicts)
@Query("Select * From depicts_table order by lastUsed DESC")
- abstract suspend fun getAllDepicts(): List
+ abstract fun getAllDepicts(): List
@Query("Select * From depicts_table order by lastUsed DESC LIMIT :n OFFSET 10")
- abstract suspend fun getDepictsForDeletion(n: Int): List
+ abstract fun getDepictsForDeletion(n: Int): List
@Delete
- abstract suspend fun delete(depicts: Depicts)
+ abstract fun delete(depicts: Depicts)
/**
* Gets all Depicts objects from the database, ordered by lastUsed in descending order.
diff --git a/app/src/main/res/resources.properties b/app/src/main/res/resources.properties
new file mode 100644
index 0000000000..d5a3ddc92a
--- /dev/null
+++ b/app/src/main/res/resources.properties
@@ -0,0 +1 @@
+unqualifiedResLocale=en-US
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0c6be31292..9cf667e460 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -544,6 +544,7 @@ Upload your first media by tapping on the add button.
Why should %1$s be deleted?
%1$s is uploaded by: %2$s
Default description language
+ Secondary Description Language
Nominating for deletion
Success
Nominated %1$s for deletion.
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 8ac890545d..aeca0d45c9 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -25,12 +25,20 @@
app:singleLineTitle="false"
android:title="@string/app_ui_language" />
+
+
+
+