mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
* 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:
parent
fcb7ccae3c
commit
06a347eee1
9 changed files with 524 additions and 207 deletions
|
|
@ -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(""));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
131
app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt
Normal file
131
app/src/main/java/fr/free/nrw/commons/upload/LanguagesAdapter.kt
Normal 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]
|
||||
|
|
@ -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]
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml
Normal file
10
app/src/main/res/drawable/ic_baseline_arrow_drop_down_24.xml
Normal 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>
|
||||
31
app/src/main/res/layout/dialog_select_language.xml
Normal file
31
app/src/main/res/layout/dialog_select_language.xml
Normal 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>
|
||||
|
|
@ -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" />
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue