Fixes #4543 - Language selection: Search (#4744)

* initial changes

* add search feature for selecting language

* add search feature for selecting language

* upload issue fix

* minor improvement

* added test and updated settings language selection ui

Co-authored-by: Pratham2305 <Pratham2305@users.noreply.github.com>
This commit is contained in:
Pratham Pahariya 2021-12-31 16:03:36 +05:30 committed by GitHub
parent fcb7ccae3c
commit 06a347eee1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 524 additions and 207 deletions

View file

@ -4,17 +4,24 @@ import static android.content.Context.MODE_PRIVATE;
import android.Manifest;
import android.app.Activity;
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ListView;
import androidx.preference.ListPreference;
import androidx.preference.MultiSelectListPreference;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroupAdapter;
import androidx.preference.PreferenceScreen;
@ -32,16 +39,17 @@ import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.logging.CommonsLogSender;
import fr.free.nrw.commons.upload.LanguagesAdapter;
import fr.free.nrw.commons.utils.PermissionUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import javax.inject.Inject;
import javax.inject.Named;
import org.wikipedia.language.AppLanguageLookUpTable;
public class SettingsFragment extends PreferenceFragmentCompat {
@ -53,8 +61,8 @@ public class SettingsFragment extends PreferenceFragmentCompat {
CommonsLogSender commonsLogSender;
private ListPreference themeListPreference;
private ListPreference descriptionLanguageListPreference;
private ListPreference appUiLanguageListPreference;
private Preference descriptionLanguageListPreference;
private Preference appUiLanguageListPreference;
private String keyLanguageListPreference;
@Override
@ -80,15 +88,50 @@ public class SettingsFragment extends PreferenceFragmentCompat {
});
}
// Gets current language code from shared preferences
String languageCode;
appUiLanguageListPreference = findPreference("appUiDefaultLanguagePref");
assert appUiLanguageListPreference != null;
keyLanguageListPreference = appUiLanguageListPreference.getKey();
prepareAppLanguages(keyLanguageListPreference);
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
appUiLanguageListPreference.setSummary(Locale.getDefault().getDisplayLanguage());
} else {
// If any language is selected by user previously, use it
Locale defLocale = new Locale(languageCode);
appUiLanguageListPreference.setSummary((defLocale).getDisplayLanguage(defLocale));
}
appUiLanguageListPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
prepareAppLanguages(appUiLanguageListPreference.getKey());
return true;
}
});
descriptionLanguageListPreference = findPreference("descriptionDefaultLanguagePref");
assert descriptionLanguageListPreference != null;
keyLanguageListPreference = descriptionLanguageListPreference.getKey();
prepareAppLanguages(keyLanguageListPreference);
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
descriptionLanguageListPreference.setSummary(Locale.getDefault().getDisplayLanguage());
} else {
// If any language is selected by user previously, use it
Locale defLocale = new Locale(languageCode);
descriptionLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
}
descriptionLanguageListPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
prepareAppLanguages(descriptionLanguageListPreference.getKey());
return true;
}
});
Preference betaTesterPreference = findPreference("becomeBetaTester");
betaTesterPreference.setOnPreferenceClickListener(preference -> {
@ -156,75 +199,98 @@ public class SettingsFragment extends PreferenceFragmentCompat {
}
/**
* Prepares language summary and language codes list and adds them to list preference as pairs.
* Prepare and Show language selection dialog box
* Uses previously saved language if there is any, if not uses phone locale as initial language.
* Disable default/already selected language from dialog box
* Get ListPreference key and act accordingly for each ListPreference.
* Adds preference changed listener and saves value chosen by user to shared preferences
* saves value chosen by user to shared preferences
* to remember later and recall MainActivity to reflect language changes
* @param keyListPreference
*/
private void prepareAppLanguages(final String keyListPreference) {
final List<String> languageNamesList;
final List<String> languageCodesList;
final AppLanguageLookUpTable appLanguageLookUpTable = new AppLanguageLookUpTable(
Objects.requireNonNull(getContext()));
languageNamesList = appLanguageLookUpTable.getLocalizedNames();
languageCodesList = appLanguageLookUpTable.getCodes();
List<String> languageNameWithCodeList = new ArrayList<>();
for (int i = 0; i < languageNamesList.size(); i++) {
languageNameWithCodeList.add(languageNamesList.get(i) + "[" + languageCodesList.get(i) + "]");
}
final CharSequence[] languageNames = languageNamesList.toArray(new CharSequence[0]);
final CharSequence[] languageCodes = languageCodesList.toArray(new CharSequence[0]);
// Add all languages and languages codes to lists preference as pair
// Gets current language code from shared preferences
final String languageCode = getCurrentLanguageCode(keyListPreference);
HashMap<Integer, String> selectedLanguages = new HashMap<>();
if (keyListPreference.equals("appUiDefaultLanguagePref")) {
appUiLanguageListPreference.setEntries(languageNames);
appUiLanguageListPreference.setEntryValues(languageCodes);
assert languageCode != null;
if (languageCode.equals("")) {
// If current language code is empty, means none selected by user yet so use phone local
appUiLanguageListPreference.setValue(Locale.getDefault().getLanguage());
selectedLanguages.put(0, Locale.getDefault().getLanguage());
} else {
// If any language is selected by user previously, use it
appUiLanguageListPreference.setValue(languageCode);
selectedLanguages.put(0, languageCode);
}
appUiLanguageListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
final String userSelectedValue = (String) newValue;
setLocale(Objects.requireNonNull(getActivity()), userSelectedValue);
saveLanguageValue(userSelectedValue, keyListPreference);
getActivity().recreate();
final Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
return true;
});
} else if (keyListPreference.equals("descriptionDefaultLanguagePref")) {
descriptionLanguageListPreference.setEntries(languageNames);
descriptionLanguageListPreference.setEntryValues(languageCodes);
assert languageCode != null;
if (languageCode.equals("")) {
// If current language code is empty, means none selected by user yet so use phone local
descriptionLanguageListPreference.setValue(Locale.getDefault().getLanguage());
selectedLanguages.put(0, Locale.getDefault().getLanguage());
} else {
// If any language is selected by user previously, use it
descriptionLanguageListPreference.setValue(languageCode);
selectedLanguages.put(0, languageCode);
}
}
LanguagesAdapter languagesAdapter = new LanguagesAdapter(
getActivity(),
selectedLanguages
);
Dialog dialog = new Dialog(getActivity());
dialog.setContentView(R.layout.dialog_select_language);
dialog.setCanceledOnTouchOutside(true);
dialog.getWindow().setLayout((int)(getActivity().getResources().getDisplayMetrics().widthPixels*0.90),
(int)(getActivity().getResources().getDisplayMetrics().heightPixels*0.90));
dialog.show();
EditText editText = dialog.findViewById(R.id.search_language);
ListView listView = dialog.findViewById(R.id.language_list);
listView.setAdapter(languagesAdapter);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1,
int i2) {
}
descriptionLanguageListPreference.setOnPreferenceChangeListener((preference, newValue) -> {
final String userSelectedValue = (String) newValue;
saveLanguageValue(userSelectedValue, keyListPreference);
return true;
});
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1,
int i2) {
languagesAdapter.getFilter().filter(charSequence);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i,
long l) {
String languageCode = ((LanguagesAdapter) adapterView.getAdapter())
.getLanguageCode(i);
saveLanguageValue(languageCode, keyListPreference);
Locale defLocale = new Locale(languageCode);
if(keyListPreference.equals("appUiDefaultLanguagePref")) {
appUiLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
setLocale(Objects.requireNonNull(getActivity()), languageCode);
getActivity().recreate();
final Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}else {
descriptionLanguageListPreference.setSummary(defLocale.getDisplayLanguage(defLocale));
}
dialog.dismiss();
}
});
dialog.setOnDismissListener(
dialogInterface -> languagesAdapter.getFilter().filter(""));
}
/**

View file

@ -0,0 +1,131 @@
package fr.free.nrw.commons.upload
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Filter
import androidx.core.os.ConfigurationCompat
import fr.free.nrw.commons.R
import fr.free.nrw.commons.utils.LangCodeUtils
import kotlinx.android.synthetic.main.row_item_languages_spinner.view.*
import org.apache.commons.lang3.StringUtils
import org.wikipedia.language.AppLanguageLookUpTable
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.LinkedHashMap
/**
* This class handles the display of language dialog and their views for UploadMediaDetailFragment
*
* @property selectedLanguages - controls the enabled state of item views
*
* @param context - required by super constructor
*/
class LanguagesAdapter constructor(
context: Context,
private val selectedLanguages: HashMap<*, String>
) : ArrayAdapter<String?>(context, R.layout.row_item_languages_spinner) {
private var languageNamesList: List<String>
private var languageCodesList: List<String>
var language: AppLanguageLookUpTable = AppLanguageLookUpTable(context)
init {
languageNamesList = language.localizedNames
languageCodesList = language.codes
}
private val filter = LanguageFilter()
var selectedLangCode = ""
override fun isEnabled(position: Int) = languageCodesList[position].let {
it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
}
override fun getCount() = languageNamesList.size
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var rowView: View
if(convertView != null) {
rowView = convertView
}else {
rowView = LayoutInflater.from(context).inflate(R.layout.row_item_languages_spinner, parent, false)
}
val languageCode = languageCodesList[position]
val languageName = languageNamesList[position]
rowView.tv_language.let {
it.isEnabled = isEnabled(position)
if (languageCode.isEmpty()) {
it.text = StringUtils.capitalize(languageName)
it.textAlignment = View.TEXT_ALIGNMENT_CENTER
} else {
it.text =
"${StringUtils.capitalize(languageName)}" +
" [${LangCodeUtils.fixLanguageCode(languageCode)}]"
}
}
return rowView
}
fun getLanguageCode(position: Int): String {
return languageCodesList[position]
}
fun getIndexOfUserDefaultLocale(context: Context): Int {
return language.codes.indexOf(context.locale.language)
}
fun getIndexOfLanguageCode(languageCode: String): Int {
return languageCodesList.indexOf(languageCode)
}
override fun getFilter() = filter
inner class LanguageFilter : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults()
val temp: LinkedHashMap<String, String> = LinkedHashMap()
if (constraint != null && language.localizedNames != null) {
val length: Int = language.localizedNames.size
var i = 0
while (i < length) {
val key: String = language.codes[i]
val value: String = language.localizedNames[i]
val defaultlanguagecode = getIndexOfUserDefaultLocale(context)
if(value.contains(constraint, true) || Locale(key).getDisplayName(
Locale(language.codes[defaultlanguagecode])).contains(constraint, true))
temp[key] = value
i++
}
filterResults.values = temp
filterResults.count = temp.size
}
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
if (results.count > 0) {
languageCodesList =
ArrayList((results.values as LinkedHashMap<String, String>).keys)
languageNamesList =
ArrayList((results.values as LinkedHashMap<String, String>).values)
notifyDataSetChanged()
} else {
languageCodesList = ArrayList()
languageNamesList = ArrayList()
notifyDataSetChanged()
}
}
}
}
private val Context.locale: Locale
get() = ConfigurationCompat.getLocales(resources.configuration)[0]

View file

@ -1,101 +0,0 @@
package fr.free.nrw.commons.upload
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.annotation.LayoutRes
import androidx.core.os.ConfigurationCompat
import fr.free.nrw.commons.R
import fr.free.nrw.commons.utils.LangCodeUtils
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.row_item_languages_spinner.*
import org.apache.commons.lang3.StringUtils
import org.wikipedia.language.AppLanguageLookUpTable
import java.util.*
/**
* This class handles the display of language spinners and their dropdown views for UploadMediaDetailFragment
*
* @property selectedLanguages - controls the enabled state of dropdown views
*
* @param context - required by super constructor
*/
class SpinnerLanguagesAdapter constructor(
context: Context,
private val selectedLanguages: HashMap<*, String>
) : ArrayAdapter<Any?>(context, -1) {
private val languageNamesList: List<String>
private val languageCodesList: List<String>
var language: AppLanguageLookUpTable = AppLanguageLookUpTable(context)
init {
languageNamesList = language.localizedNames;
languageCodesList = language.codes;
}
var selectedLangCode = ""
override fun isEnabled(position: Int) = languageCodesList[position].let {
it.isNotEmpty() && !selectedLanguages.containsValue(it) && it != selectedLangCode
}
override fun getCount() = languageNamesList.size
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup) =
(convertView ?: parent.inflate(R.layout.row_item_languages_spinner).also {
it.tag = DropDownViewHolder(it)
}).apply {
(tag as DropDownViewHolder).init(
languageCodesList[position],
languageNamesList[position],
isEnabled(position)
)
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup) =
(convertView ?: parent.inflate(R.layout.row_item_languages_spinner).also {
it.tag = SpinnerViewHolder(it)
}).apply { (tag as SpinnerViewHolder).init(languageCodesList[position]) }
class SpinnerViewHolder(override val containerView: View) : LayoutContainer {
fun init(languageCode: String) {
LangCodeUtils.fixLanguageCode(languageCode).let {
tv_language.text = if (it.length > 2) it.take(2) else it
}
}
}
class DropDownViewHolder(override val containerView: View) : LayoutContainer {
fun init(languageCode: String, languageName: String, enabled: Boolean) {
tv_language.isEnabled = enabled
if (languageCode.isEmpty()) {
tv_language.text = StringUtils.capitalize(languageName)
tv_language.textAlignment = View.TEXT_ALIGNMENT_CENTER
} else {
tv_language.text =
"${StringUtils.capitalize(languageName)}" +
" [${LangCodeUtils.fixLanguageCode(languageCode)}]"
}
}
}
fun getLanguageCode(position: Int): String {
return languageCodesList[position]
}
fun getIndexOfUserDefaultLocale(context: Context): Int {
return languageCodesList.indexOf(context.locale.language)
}
fun getIndexOfLanguageCode(languageCode: String): Int {
return languageCodesList.indexOf(languageCode)
}
}
private fun ViewGroup.inflate(@LayoutRes resId: Int) =
LayoutInflater.from(context).inflate(resId, this, false)
private val Context.locale: Locale
get() = ConfigurationCompat.getLocales(resources.configuration)[0]

View file

@ -1,13 +1,19 @@
package fr.free.nrw.commons.upload;
import android.app.Dialog;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
@ -28,7 +34,7 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
private Callback callback;
private EventListener eventListener;
private HashMap<AdapterView, String> selectedLanguages;
private HashMap<Integer, String> selectedLanguages;
private final String savedLanguageValue;
public UploadMediaDetailAdapter(String savedLanguageValue) {
@ -80,6 +86,7 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
}
public void addDescription(UploadMediaDetail uploadMediaDetail) {
selectedLanguages.put(uploadMediaDetails.size(), "en");
this.uploadMediaDetails.add(uploadMediaDetail);
notifyItemInserted(uploadMediaDetails.size());
}
@ -91,6 +98,7 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
* @param position
*/
public void removeDescription(final UploadMediaDetail uploadMediaDetail, final int position) {
selectedLanguages.remove(position);
this.uploadMediaDetails.remove(uploadMediaDetail);
notifyItemRemoved(position);
}
@ -98,8 +106,8 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
public class ViewHolder extends RecyclerView.ViewHolder {
@Nullable
@BindView(R.id.spinner_description_languages)
Spinner spinnerDescriptionLanguages;
@BindView(R.id.description_languages)
TextView descriptionLanguages;
@BindView(R.id.description_item_edit_text)
PasteSensitiveTextInputEditText descItemEditText;
@ -126,6 +134,7 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
UploadMediaDetail uploadMediaDetail = uploadMediaDetails.get(position);
Timber.d("UploadMediaDetail is " + uploadMediaDetail);
descriptionLanguages.setFocusable(false);
captionItemEditText.addTextChangedListener(new AbstractTextWatcher(
value -> {
if (position == 0) {
@ -157,11 +166,11 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
captionItemEditText.addTextChangedListener(new AbstractTextWatcher(
captionText -> uploadMediaDetails.get(position).setCaptionText(captionText)));
initLanguageSpinner(position, uploadMediaDetail);
initLanguage(position, uploadMediaDetail);
descItemEditText.addTextChangedListener(new AbstractTextWatcher(
descriptionText -> uploadMediaDetails.get(position).setDescriptionText(descriptionText)));
initLanguageSpinner(position, uploadMediaDetail);
initLanguage(position, uploadMediaDetail);
//If the description was manually added by the user, it deserves focus, if not, let the user decide
if (uploadMediaDetail.isManuallyAdded()) {
@ -171,78 +180,124 @@ public class UploadMediaDetailAdapter extends RecyclerView.Adapter<UploadMediaDe
}
}
/**
* Extracted out the function to init the language spinner with different system supported languages
* @param position
* @param description
*/
private void initLanguageSpinner(int position, UploadMediaDetail description) {
SpinnerLanguagesAdapter languagesAdapter = new SpinnerLanguagesAdapter(
spinnerDescriptionLanguages.getContext(),
selectedLanguages
private void initLanguage(int position, UploadMediaDetail description) {
LanguagesAdapter languagesAdapter = new LanguagesAdapter(
descriptionLanguages.getContext(),
selectedLanguages
);
spinnerDescriptionLanguages.setAdapter(languagesAdapter);
spinnerDescriptionLanguages.setOnItemSelectedListener(new OnItemSelectedListener() {
descriptionLanguages.setOnClickListener(new OnClickListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
long l) {
description.setSelectedLanguageIndex(position);
String languageCode = ((SpinnerLanguagesAdapter) adapterView.getAdapter())
.getLanguageCode(position);
description.setLanguageCode(languageCode);
selectedLanguages.remove(adapterView);
selectedLanguages.put(adapterView, languageCode);
((SpinnerLanguagesAdapter) adapterView
.getAdapter()).setSelectedLangCode(languageCode);
spinnerDescriptionLanguages.setSelection(position);
Timber.d("Description language code is: " + languageCode);
}
public void onClick(View view) {
Dialog dialog = new Dialog(view.getContext());
dialog.setContentView(R.layout.dialog_select_language);
dialog.setCanceledOnTouchOutside(true);
dialog.getWindow().setLayout((int)(view.getContext().getResources().getDisplayMetrics().widthPixels*0.90),
(int)(view.getContext().getResources().getDisplayMetrics().heightPixels*0.90));
dialog.show();
EditText editText = dialog.findViewById(R.id.search_language);
ListView listView = dialog.findViewById(R.id.language_list);
listView.setAdapter(languagesAdapter);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1,
int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1,
int i2) {
languagesAdapter.getFilter().filter(charSequence);
}
@Override
public void afterTextChanged(Editable editable) {
}
});
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i,
long l) {
description.setSelectedLanguageIndex(i);
String languageCode = ((LanguagesAdapter) adapterView.getAdapter())
.getLanguageCode(i);
description.setLanguageCode(languageCode);
selectedLanguages.remove(position);
selectedLanguages.put(position, languageCode);
((LanguagesAdapter) adapterView
.getAdapter()).setSelectedLangCode(languageCode);
Timber.d("Description language code is: " + languageCode);
descriptionLanguages.setText(languageCode);
dialog.dismiss();
}
});
dialog.setOnDismissListener(
dialogInterface -> languagesAdapter.getFilter().filter(""));
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
if (description.getSelectedLanguageIndex() == -1) {
if (!TextUtils.isEmpty(savedLanguageValue)) {
// If user has chosen a default language from settings activity
// savedLanguageValue is not null
if(!TextUtils.isEmpty(description.getLanguageCode())) {
spinnerDescriptionLanguages.setSelection(languagesAdapter
.getIndexOfLanguageCode(description.getLanguageCode()));
descriptionLanguages.setText(description.getLanguageCode());
selectedLanguages.remove(position);
selectedLanguages.put(position, description.getLanguageCode());
} else {
spinnerDescriptionLanguages.setSelection(languagesAdapter
.getIndexOfLanguageCode(savedLanguageValue));
description.setLanguageCode(savedLanguageValue);
descriptionLanguages.setText(savedLanguageValue);
selectedLanguages.remove(position);
selectedLanguages.put(position, savedLanguageValue);
}
} else if (!TextUtils.isEmpty(description.getLanguageCode())) {
spinnerDescriptionLanguages.setSelection(languagesAdapter
.getIndexOfLanguageCode(description.getLanguageCode()));
descriptionLanguages.setText(description.getLanguageCode());
selectedLanguages.remove(position);
selectedLanguages.put(position, description.getLanguageCode());
} else {
//Checking whether Language Code attribute is null or not.
if (uploadMediaDetails.get(position).getLanguageCode() != null) {
//If it is not null that means it is fetching details from the previous
// upload (i.e. when user has pressed copy previous caption & description)
//hence providing same language code for the current upload.
spinnerDescriptionLanguages.setSelection(languagesAdapter
.getIndexOfLanguageCode(uploadMediaDetails.get(position)
.getLanguageCode()), true);
descriptionLanguages.setText(uploadMediaDetails.get(position)
.getLanguageCode());
selectedLanguages.remove(position);
selectedLanguages.put(position, uploadMediaDetails.get(position)
.getLanguageCode());
} else {
if (position == 0) {
final int defaultLocaleIndex = languagesAdapter
.getIndexOfUserDefaultLocale(spinnerDescriptionLanguages
.getContext());
spinnerDescriptionLanguages.setSelection(defaultLocaleIndex, true);
.getIndexOfUserDefaultLocale(descriptionLanguages
.getContext());
descriptionLanguages
.setText(languagesAdapter.getLanguageCode(defaultLocaleIndex));
description.setLanguageCode(languagesAdapter.getLanguageCode(defaultLocaleIndex));
selectedLanguages.remove(position);
selectedLanguages.put(position, languagesAdapter.getLanguageCode(defaultLocaleIndex));
} else {
spinnerDescriptionLanguages.setSelection(0, true);
description.setLanguageCode(languagesAdapter.getLanguageCode(0));
descriptionLanguages.setText(languagesAdapter.getLanguageCode(0));
selectedLanguages.remove(position);
selectedLanguages.put(position, languagesAdapter.getLanguageCode(0));
}
}
}
} else {
spinnerDescriptionLanguages.setSelection(description.getSelectedLanguageIndex());
selectedLanguages.put(spinnerDescriptionLanguages, description.getLanguageCode());
descriptionLanguages.setText(description.getLanguageCode());
selectedLanguages.remove(position);
selectedLanguages.put(position, description.getLanguageCode());
}
}
}

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/search_language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:hint="Type Language Name"
android:padding="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"></EditText>
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:id="@+id/language_list"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search_language" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -23,11 +23,14 @@
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_remove" />
<Spinner
android:id="@+id/spinner_description_languages"
<TextView
android:id="@+id/description_languages"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:spinnerMode="dialog"
android:clickable="true"
android:drawableRight="@drawable/ic_baseline_arrow_drop_down_24"
android:padding="@dimen/dimen_2"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/caption_item_edit_text_input_layout"
app:layout_constraintStart_toStartOf="parent" />

View file

@ -26,13 +26,13 @@
android:summary="@string/use_external_storage_summary"
android:title="@string/use_external_storage" />
<ListPreference
<Preference
android:key="appUiDefaultLanguagePref"
app:useSimpleSummaryProvider="true"
app:singleLineTitle="false"
android:title="@string/app_ui_language" />
<ListPreference
<Preference
android:key="descriptionDefaultLanguagePref"
app:useSimpleSummaryProvider="true"
app:singleLineTitle="false"

View file

@ -0,0 +1,122 @@
package fr.free.nrw.commons.upload
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.ConfigurationCompat
import fr.free.nrw.commons.R
import fr.free.nrw.commons.TestCommonsApplication
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.Assertions
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.annotation.Config
import org.robolectric.annotation.LooperMode
import org.wikipedia.language.AppLanguageLookUpTable
import java.util.*
@RunWith(RobolectricTestRunner::class)
@Config(sdk = [21], application = TestCommonsApplication::class)
@LooperMode(LooperMode.Mode.PAUSED)
class LanguagesAdapterTest {
private lateinit var context: Context
@Mock
private lateinit var selectedLanguages: HashMap<Integer, String>
@Mock
private lateinit var parent: ViewGroup
private lateinit var languageNamesList: List<String>
private lateinit var languageCodesList: List<String>
private lateinit var language: AppLanguageLookUpTable
private lateinit var languagesAdapter: LanguagesAdapter
private lateinit var convertView: View
private var selectLanguages: HashMap<Integer, String> = HashMap()
@Before
@Throws(Exception::class)
fun setUp() {
MockitoAnnotations.initMocks(this)
context = RuntimeEnvironment.application.applicationContext
language = AppLanguageLookUpTable(context)
convertView = LayoutInflater.from(context)
.inflate(R.layout.row_item_languages_spinner, null) as View
languageNamesList = language.localizedNames
languageCodesList = language.codes
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
}
@Test
@Throws(Exception::class)
fun testOnGetView() {
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
`when`(selectedLanguages.values).thenReturn(Collections.emptyList())
Assert.assertEquals(languagesAdapter.getView(0, convertView, parent), convertView)
}
@Test
fun testGetCount() {
Assertions.assertEquals(languageCodesList.size, languagesAdapter.count)
}
@Test
fun testGetLanguageCode() {
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
Assertions.assertEquals(languagesAdapter.getLanguageCode(0), languageCodesList[0])
}
@Test
fun testGetIndexOfUserDefaultLocale() {
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
Assertions.assertEquals(languageCodesList.indexOf(ConfigurationCompat.getLocales(context.resources.configuration)[0].language), languagesAdapter.getIndexOfUserDefaultLocale(context))
}
@Test
fun testSelectLanguageNotEmpty() {
selectLanguages[Integer(0)] = "es"
selectLanguages[Integer(1)] = "de"
languagesAdapter = LanguagesAdapter(context, selectLanguages)
Assertions.assertEquals(false, languagesAdapter.isEnabled(languagesAdapter.getIndexOfLanguageCode("es")))
Assertions.assertEquals(false, languagesAdapter.isEnabled(languagesAdapter.getIndexOfLanguageCode("de")))
}
@Test
fun testFilterEmpty() {
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
languagesAdapter.filter.filter("")
Assertions.assertEquals(languageCodesList.size, languagesAdapter.count)
}
@Test
fun testFilterNonEmpty() {
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
val constraint = "spa"
languagesAdapter.filter.filter(constraint)
val length: Int = languageNamesList.size
val defaultlanguagecode = languageCodesList.indexOf(ConfigurationCompat.getLocales(context.resources.configuration)[0].language)
var i = 0
var s = 0
while (i < length) {
val key: String = language.codes[i]
val value: String = language.localizedNames[i]
if(value.contains(constraint, true) || Locale(key).getDisplayName(
Locale(language.codes[defaultlanguagecode])).contains(constraint, true))
s++
i++
}
Assertions.assertEquals(s, languagesAdapter.count)
}
}