mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 21:33:53 +01:00
Convert UploadMediaDetailAdapter to kotlin
This commit is contained in:
parent
a6c4731f74
commit
87c8224793
8 changed files with 577 additions and 652 deletions
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.description
|
||||
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.speech.RecognizerIntent
|
||||
|
|
@ -72,7 +71,7 @@ class DescriptionEditActivity :
|
|||
|
||||
private lateinit var binding: ActivityDescriptionEditBinding
|
||||
|
||||
private var descriptionAndCaptions: ArrayList<UploadMediaDetail>? = null
|
||||
private var descriptionAndCaptions: MutableList<UploadMediaDetail>? = null
|
||||
|
||||
private val voiceInputResultLauncher = registerForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
|
|
@ -114,22 +113,18 @@ class DescriptionEditActivity :
|
|||
* Initializes the RecyclerView
|
||||
* @param descriptionAndCaptions list of description and caption
|
||||
*/
|
||||
private fun initRecyclerView(descriptionAndCaptions: ArrayList<UploadMediaDetail>?) {
|
||||
private fun initRecyclerView(descriptionAndCaptions: MutableList<UploadMediaDetail>?) {
|
||||
uploadMediaDetailAdapter =
|
||||
UploadMediaDetailAdapter(
|
||||
this,
|
||||
savedLanguageValue,
|
||||
descriptionAndCaptions,
|
||||
descriptionAndCaptions ?: mutableListOf(),
|
||||
recentLanguagesDao,
|
||||
voiceInputResultLauncher
|
||||
)
|
||||
uploadMediaDetailAdapter.setCallback { titleStringID: Int, messageStringId: Int ->
|
||||
showInfoAlert(
|
||||
titleStringID,
|
||||
messageStringId,
|
||||
)
|
||||
}
|
||||
uploadMediaDetailAdapter.setEventListener(this)
|
||||
|
||||
uploadMediaDetailAdapter.callback = UploadMediaDetailAdapter.Callback(::showInfoAlert)
|
||||
uploadMediaDetailAdapter.eventListener = this
|
||||
rvDescriptions = binding.rvDescriptionsCaptions
|
||||
rvDescriptions!!.layoutManager = LinearLayoutManager(this)
|
||||
rvDescriptions!!.adapter = uploadMediaDetailAdapter
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import java.util.HashMap
|
|||
class RecentLanguagesAdapter constructor(
|
||||
context: Context,
|
||||
var recentLanguages: List<Language>,
|
||||
private val selectedLanguages: HashMap<*, String>,
|
||||
private val selectedLanguages: MutableMap<Int, String>,
|
||||
) : ArrayAdapter<String?>(context, R.layout.row_item_languages_spinner) {
|
||||
/**
|
||||
* Selected language code in UploadMediaDetailAdapter
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import java.util.Locale
|
|||
*/
|
||||
class LanguagesAdapter constructor(
|
||||
context: Context,
|
||||
private val selectedLanguages: HashMap<*, String>,
|
||||
private val selectedLanguages: MutableMap<Int, String>,
|
||||
) : ArrayAdapter<String?>(context, R.layout.row_item_languages_spinner) {
|
||||
companion object {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,633 +0,0 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
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.OnItemClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.databinding.RowItemDescriptionBinding;
|
||||
import fr.free.nrw.commons.recentlanguages.Language;
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter;
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao;
|
||||
import fr.free.nrw.commons.ui.PasteSensitiveTextInputEditText;
|
||||
import fr.free.nrw.commons.utils.AbstractTextWatcher;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class UploadMediaDetailAdapter extends
|
||||
RecyclerView.Adapter<UploadMediaDetailAdapter.ViewHolder> {
|
||||
|
||||
RecentLanguagesDao recentLanguagesDao;
|
||||
|
||||
private List<UploadMediaDetail> uploadMediaDetails;
|
||||
private Callback callback;
|
||||
private EventListener eventListener;
|
||||
|
||||
private HashMap<Integer, String> selectedLanguages;
|
||||
private final String savedLanguageValue;
|
||||
private TextView recentLanguagesTextView;
|
||||
private View separator;
|
||||
private ListView languageHistoryListView;
|
||||
private int currentPosition;
|
||||
private Fragment fragment;
|
||||
private Activity activity;
|
||||
private final ActivityResultLauncher<Intent> voiceInputResultLauncher;
|
||||
private SelectedVoiceIcon selectedVoiceIcon;
|
||||
|
||||
private RowItemDescriptionBinding binding;
|
||||
|
||||
public UploadMediaDetailAdapter(Fragment fragment, String savedLanguageValue,
|
||||
RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
|
||||
uploadMediaDetails = new ArrayList<>();
|
||||
selectedLanguages = new HashMap<>();
|
||||
this.savedLanguageValue = savedLanguageValue;
|
||||
this.recentLanguagesDao = recentLanguagesDao;
|
||||
this.fragment = fragment;
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher;
|
||||
}
|
||||
|
||||
public UploadMediaDetailAdapter(Activity activity, final String savedLanguageValue,
|
||||
List<UploadMediaDetail> uploadMediaDetails, RecentLanguagesDao recentLanguagesDao, ActivityResultLauncher<Intent> voiceInputResultLauncher) {
|
||||
this.uploadMediaDetails = uploadMediaDetails;
|
||||
selectedLanguages = new HashMap<>();
|
||||
this.savedLanguageValue = savedLanguageValue;
|
||||
this.recentLanguagesDao = recentLanguagesDao;
|
||||
this.activity = activity;
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher;
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public void setEventListener(EventListener eventListener) {
|
||||
this.eventListener = eventListener;
|
||||
}
|
||||
|
||||
public void setItems(List<UploadMediaDetail> uploadMediaDetails) {
|
||||
this.uploadMediaDetails = uploadMediaDetails;
|
||||
selectedLanguages = new HashMap<>();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public List<UploadMediaDetail> getItems() {
|
||||
return uploadMediaDetails;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
binding = RowItemDescriptionBinding.inflate(inflater, parent, false);
|
||||
return new ViewHolder(binding.getRoot());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a workaround for a known bug by android here
|
||||
* https://issuetracker.google.com/issues/37095917 makes the edit text on second and subsequent
|
||||
* fragments inside an adapter receptive to long click for copy/paste options
|
||||
*
|
||||
* @param holder the view holder
|
||||
*/
|
||||
@Override
|
||||
public void onViewAttachedToWindow(@NonNull final ViewHolder holder) {
|
||||
super.onViewAttachedToWindow(holder);
|
||||
holder.captionItemEditText.setEnabled(false);
|
||||
holder.captionItemEditText.setEnabled(true);
|
||||
holder.descItemEditText.setEnabled(false);
|
||||
holder.descItemEditText.setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
holder.bind(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return uploadMediaDetails.size();
|
||||
}
|
||||
|
||||
public void addDescription(UploadMediaDetail uploadMediaDetail) {
|
||||
selectedLanguages.put(uploadMediaDetails.size(), "en");
|
||||
this.uploadMediaDetails.add(uploadMediaDetail);
|
||||
notifyItemInserted(uploadMediaDetails.size());
|
||||
}
|
||||
|
||||
private void startSpeechInput(String locale) {
|
||||
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
intent.putExtra(
|
||||
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
|
||||
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
|
||||
);
|
||||
intent.putExtra(
|
||||
RecognizerIntent.EXTRA_LANGUAGE,
|
||||
locale
|
||||
);
|
||||
|
||||
try {
|
||||
voiceInputResultLauncher.launch(intent);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the result of the speech input by processing the spoken text.
|
||||
* If the spoken text is not empty, it capitalizes the first letter of the spoken text
|
||||
* and updates the appropriate field (caption or description) of the current
|
||||
* UploadMediaDetail based on the selected voice icon.
|
||||
* Finally, it notifies the adapter that the data set has changed.
|
||||
*
|
||||
* @param spokenText the text input received from speech recognition.
|
||||
*/
|
||||
public void handleSpeechResult(String spokenText) {
|
||||
if (!spokenText.isEmpty()) {
|
||||
String spokenTextCapitalized =
|
||||
spokenText.substring(0, 1).toUpperCase() + spokenText.substring(1);
|
||||
if (currentPosition < uploadMediaDetails.size()) {
|
||||
UploadMediaDetail uploadMediaDetail = uploadMediaDetails.get(currentPosition);
|
||||
switch (selectedVoiceIcon) {
|
||||
case CAPTION:
|
||||
uploadMediaDetail.setCaptionText(spokenTextCapitalized);
|
||||
break;
|
||||
case DESCRIPTION:
|
||||
uploadMediaDetail.setDescriptionText(spokenTextCapitalized);
|
||||
break;
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove description based on position from the list and notifies the RecyclerView Adapter that
|
||||
* data in adapter has been removed at that particular position.
|
||||
*
|
||||
* @param uploadMediaDetail
|
||||
* @param position
|
||||
*/
|
||||
public void removeDescription(final UploadMediaDetail uploadMediaDetail, final int position) {
|
||||
selectedLanguages.remove(position);
|
||||
this.uploadMediaDetails.remove(uploadMediaDetail);
|
||||
int i = position + 1;
|
||||
while (selectedLanguages.containsKey(i)) {
|
||||
selectedLanguages.remove(i);
|
||||
i++;
|
||||
}
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, uploadMediaDetails.size() - position);
|
||||
updateAddButtonVisibility();
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
TextView descriptionLanguages ;
|
||||
|
||||
PasteSensitiveTextInputEditText descItemEditText;
|
||||
|
||||
TextInputLayout descInputLayout;
|
||||
|
||||
PasteSensitiveTextInputEditText captionItemEditText;
|
||||
|
||||
TextInputLayout captionInputLayout;
|
||||
|
||||
ImageView removeButton;
|
||||
|
||||
ImageView addButton;
|
||||
|
||||
ConstraintLayout clParent;
|
||||
|
||||
LinearLayout betterCaptionLinearLayout;
|
||||
|
||||
LinearLayout betterDescriptionLinearLayout;
|
||||
|
||||
private
|
||||
|
||||
AbstractTextWatcher captionListener;
|
||||
|
||||
AbstractTextWatcher descriptionListener;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
Timber.i("descItemEditText:" + descItemEditText);
|
||||
}
|
||||
|
||||
public void bind(int position) {
|
||||
UploadMediaDetail uploadMediaDetail = uploadMediaDetails.get(position);
|
||||
Timber.d("UploadMediaDetail is " + uploadMediaDetail);
|
||||
|
||||
descriptionLanguages = binding.descriptionLanguages;
|
||||
descItemEditText = binding.descriptionItemEditText;
|
||||
descInputLayout = binding.descriptionItemEditTextInputLayout;
|
||||
captionItemEditText = binding.captionItemEditText;
|
||||
captionInputLayout = binding.captionItemEditTextInputLayout;
|
||||
removeButton = binding.btnRemove;
|
||||
addButton = binding.btnAdd;
|
||||
clParent = binding.clParent;
|
||||
betterCaptionLinearLayout = binding.llWriteBetterCaption;
|
||||
betterDescriptionLinearLayout = binding.llWriteBetterDescription;
|
||||
|
||||
|
||||
descriptionLanguages.setFocusable(false);
|
||||
captionItemEditText.addTextChangedListener(new AbstractTextWatcher(
|
||||
value -> {
|
||||
if (position == 0) {
|
||||
eventListener.onPrimaryCaptionTextChange(value.length() != 0);
|
||||
}
|
||||
}));
|
||||
captionItemEditText.removeTextChangedListener(captionListener);
|
||||
descItemEditText.removeTextChangedListener(descriptionListener);
|
||||
captionItemEditText.setText(uploadMediaDetail.getCaptionText());
|
||||
descItemEditText.setText(uploadMediaDetail.getDescriptionText());
|
||||
captionInputLayout.setEndIconMode(TextInputLayout.END_ICON_CUSTOM);
|
||||
captionInputLayout.setEndIconDrawable(R.drawable.baseline_keyboard_voice);
|
||||
captionInputLayout.setEndIconOnClickListener(v -> {
|
||||
currentPosition = position;
|
||||
selectedVoiceIcon = SelectedVoiceIcon.CAPTION;
|
||||
startSpeechInput(descriptionLanguages.getText().toString());
|
||||
});
|
||||
descInputLayout.setEndIconMode(TextInputLayout.END_ICON_CUSTOM);
|
||||
descInputLayout.setEndIconDrawable(R.drawable.baseline_keyboard_voice);
|
||||
descInputLayout.setEndIconOnClickListener(v -> {
|
||||
currentPosition = position;
|
||||
selectedVoiceIcon = SelectedVoiceIcon.DESCRIPTION;
|
||||
startSpeechInput(descriptionLanguages.getText().toString());
|
||||
});
|
||||
|
||||
if (position == 0) {
|
||||
removeButton.setVisibility(View.GONE);
|
||||
betterCaptionLinearLayout.setVisibility(View.VISIBLE);
|
||||
betterCaptionLinearLayout.setOnClickListener(
|
||||
v -> callback.showAlert(R.string.media_detail_caption, R.string.caption_info));
|
||||
betterDescriptionLinearLayout.setVisibility(View.VISIBLE);
|
||||
betterDescriptionLinearLayout.setOnClickListener(
|
||||
v -> callback.showAlert(R.string.media_detail_description,
|
||||
R.string.description_info));
|
||||
Objects.requireNonNull(captionInputLayout.getEditText())
|
||||
.setFilters(new InputFilter[]{
|
||||
new UploadMediaDetailInputFilter()
|
||||
});
|
||||
} else {
|
||||
removeButton.setVisibility(View.VISIBLE);
|
||||
betterCaptionLinearLayout.setVisibility(View.GONE);
|
||||
betterDescriptionLinearLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
removeButton.setOnClickListener(v -> removeDescription(uploadMediaDetail, position));
|
||||
captionListener = new AbstractTextWatcher(
|
||||
captionText -> uploadMediaDetail.setCaptionText(
|
||||
convertIdeographicSpaceToLatinSpace(captionText.strip()))
|
||||
);
|
||||
descriptionListener = new AbstractTextWatcher(
|
||||
descriptionText -> uploadMediaDetail.setDescriptionText(descriptionText));
|
||||
captionItemEditText.addTextChangedListener(captionListener);
|
||||
initLanguage(position, uploadMediaDetail);
|
||||
|
||||
descItemEditText.addTextChangedListener(descriptionListener);
|
||||
initLanguage(position, uploadMediaDetail);
|
||||
|
||||
if (fragment != null) {
|
||||
FrameLayout.LayoutParams newLayoutParams = (FrameLayout.LayoutParams) clParent.getLayoutParams();
|
||||
newLayoutParams.topMargin = 0;
|
||||
newLayoutParams.leftMargin = 0;
|
||||
newLayoutParams.rightMargin = 0;
|
||||
newLayoutParams.bottomMargin = 0;
|
||||
clParent.setLayoutParams(newLayoutParams);
|
||||
}
|
||||
updateAddButtonVisibility();
|
||||
addButton.setOnClickListener(v -> eventListener.addLanguage());
|
||||
|
||||
//If the description was manually added by the user, it deserves focus, if not, let the user decide
|
||||
if (uploadMediaDetail.isManuallyAdded()) {
|
||||
captionItemEditText.requestFocus();
|
||||
} else {
|
||||
captionItemEditText.clearFocus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void initLanguage(int position, UploadMediaDetail description) {
|
||||
|
||||
final List<Language> recentLanguages = recentLanguagesDao.getRecentLanguages();
|
||||
|
||||
LanguagesAdapter languagesAdapter = new LanguagesAdapter(
|
||||
descriptionLanguages.getContext(),
|
||||
selectedLanguages
|
||||
);
|
||||
|
||||
descriptionLanguages.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Dialog dialog = new Dialog(view.getContext());
|
||||
dialog.setContentView(R.layout.dialog_select_language);
|
||||
dialog.setCancelable(false);
|
||||
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);
|
||||
final Button cancelButton = dialog.findViewById(R.id.cancel_button);
|
||||
languageHistoryListView = dialog.findViewById(R.id.language_history_list);
|
||||
recentLanguagesTextView = dialog.findViewById(R.id.recent_searches);
|
||||
separator = dialog.findViewById(R.id.separator);
|
||||
setUpRecentLanguagesSection(recentLanguages);
|
||||
|
||||
listView.setAdapter(languagesAdapter);
|
||||
|
||||
cancelButton.setOnClickListener(v -> dialog.dismiss());
|
||||
|
||||
editText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1,
|
||||
int i2) {
|
||||
hideRecentLanguagesSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1,
|
||||
int i2) {
|
||||
languagesAdapter.getFilter().filter(charSequence);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
languageHistoryListView.setOnItemClickListener(
|
||||
(adapterView, view1, position, id) -> {
|
||||
onRecentLanguageClicked(dialog, adapterView, position, description);
|
||||
});
|
||||
|
||||
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);
|
||||
final String languageName
|
||||
= ((LanguagesAdapter) adapterView.getAdapter()).getLanguageName(i);
|
||||
final boolean isExists
|
||||
= recentLanguagesDao.findRecentLanguage(languageCode);
|
||||
if (isExists) {
|
||||
recentLanguagesDao.deleteRecentLanguage(languageCode);
|
||||
}
|
||||
recentLanguagesDao
|
||||
.addRecentLanguage(new Language(languageName, languageCode));
|
||||
|
||||
selectedLanguages.clear();
|
||||
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(""));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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())) {
|
||||
descriptionLanguages.setText(description.getLanguageCode());
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.put(position, description.getLanguageCode());
|
||||
} else {
|
||||
description.setLanguageCode(savedLanguageValue);
|
||||
descriptionLanguages.setText(savedLanguageValue);
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.put(position, savedLanguageValue);
|
||||
}
|
||||
} else if (!TextUtils.isEmpty(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.
|
||||
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(descriptionLanguages
|
||||
.getContext());
|
||||
descriptionLanguages
|
||||
.setText(languagesAdapter.getLanguageCode(defaultLocaleIndex));
|
||||
description.setLanguageCode(
|
||||
languagesAdapter.getLanguageCode(defaultLocaleIndex));
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.put(position,
|
||||
languagesAdapter.getLanguageCode(defaultLocaleIndex));
|
||||
} else {
|
||||
description.setLanguageCode(languagesAdapter.getLanguageCode(0));
|
||||
descriptionLanguages.setText(languagesAdapter.getLanguageCode(0));
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.put(position, languagesAdapter.getLanguageCode(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
descriptionLanguages.setText(description.getLanguageCode());
|
||||
selectedLanguages.remove(position);
|
||||
selectedLanguages.put(position, description.getLanguageCode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles click event for recent language section
|
||||
*/
|
||||
private void onRecentLanguageClicked(final Dialog dialog, final AdapterView<?> adapterView,
|
||||
final int position, final UploadMediaDetail description) {
|
||||
description.setSelectedLanguageIndex(position);
|
||||
final String languageCode = ((RecentLanguagesAdapter) adapterView.getAdapter())
|
||||
.getLanguageCode(position);
|
||||
description.setLanguageCode(languageCode);
|
||||
final String languageName = ((RecentLanguagesAdapter) adapterView.getAdapter())
|
||||
.getLanguageName(position);
|
||||
final boolean isExists = recentLanguagesDao.findRecentLanguage(languageCode);
|
||||
if (isExists) {
|
||||
recentLanguagesDao.deleteRecentLanguage(languageCode);
|
||||
}
|
||||
recentLanguagesDao.addRecentLanguage(new Language(languageName, languageCode));
|
||||
|
||||
selectedLanguages.clear();
|
||||
selectedLanguages.put(position, languageCode);
|
||||
((RecentLanguagesAdapter) adapterView
|
||||
.getAdapter()).setSelectedLangCode(languageCode);
|
||||
Timber.d("Description language code is: %s", languageCode);
|
||||
if (descriptionLanguages!=null) {
|
||||
descriptionLanguages.setText(languageCode);
|
||||
}
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides recent languages section
|
||||
*/
|
||||
private void hideRecentLanguagesSection() {
|
||||
languageHistoryListView.setVisibility(View.GONE);
|
||||
recentLanguagesTextView.setVisibility(View.GONE);
|
||||
separator.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up recent languages section
|
||||
*
|
||||
* @param recentLanguages recently used languages
|
||||
*/
|
||||
private void setUpRecentLanguagesSection(final List<Language> recentLanguages) {
|
||||
if (recentLanguages.isEmpty()) {
|
||||
languageHistoryListView.setVisibility(View.GONE);
|
||||
recentLanguagesTextView.setVisibility(View.GONE);
|
||||
separator.setVisibility(View.GONE);
|
||||
} else {
|
||||
if (recentLanguages.size() > 5) {
|
||||
for (int i = recentLanguages.size() - 1; i >= 5; i--) {
|
||||
recentLanguagesDao.deleteRecentLanguage(recentLanguages.get(i)
|
||||
.getLanguageCode());
|
||||
}
|
||||
}
|
||||
languageHistoryListView.setVisibility(View.VISIBLE);
|
||||
recentLanguagesTextView.setVisibility(View.VISIBLE);
|
||||
separator.setVisibility(View.VISIBLE);
|
||||
|
||||
if (descriptionLanguages!=null) {
|
||||
final RecentLanguagesAdapter recentLanguagesAdapter
|
||||
= new RecentLanguagesAdapter(
|
||||
descriptionLanguages.getContext(),
|
||||
recentLanguagesDao.getRecentLanguages(),
|
||||
selectedLanguages);
|
||||
languageHistoryListView.setAdapter(recentLanguagesAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Ideographic space to Latin space
|
||||
*
|
||||
* @param source the source text
|
||||
* @return a string with Latin spaces instead of Ideographic spaces
|
||||
*/
|
||||
public String convertIdeographicSpaceToLatinSpace(String source) {
|
||||
Pattern ideographicSpacePattern = Pattern.compile("\\x{3000}");
|
||||
return ideographicSpacePattern.matcher(source).replaceAll(" ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the visibility of the "Add" button for all items in the RecyclerView except
|
||||
* the last item in RecyclerView
|
||||
*/
|
||||
private void updateAddButtonVisibility() {
|
||||
int lastItemPosition = getItemCount() - 1;
|
||||
// Hide Add Button for all items
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
if (fragment != null) {
|
||||
if (fragment.getView() != null) {
|
||||
ViewHolder holder = (ViewHolder) ((RecyclerView) fragment.getView()
|
||||
.findViewById(R.id.rv_descriptions)).findViewHolderForAdapterPosition(i);
|
||||
if (holder != null) {
|
||||
holder.addButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.activity != null) {
|
||||
ViewHolder holder = (ViewHolder) ((RecyclerView) activity.findViewById(
|
||||
R.id.rv_descriptions_captions)).findViewHolderForAdapterPosition(i);
|
||||
if (holder != null) {
|
||||
holder.addButton.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show Add Button for the last item
|
||||
if (fragment != null) {
|
||||
if (fragment.getView() != null) {
|
||||
ViewHolder lastItemHolder = (ViewHolder) ((RecyclerView) fragment.getView()
|
||||
.findViewById(R.id.rv_descriptions)).findViewHolderForAdapterPosition(
|
||||
lastItemPosition);
|
||||
if (lastItemHolder != null) {
|
||||
lastItemHolder.addButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.activity != null) {
|
||||
ViewHolder lastItemHolder = (ViewHolder) ((RecyclerView) activity
|
||||
.findViewById(R.id.rv_descriptions_captions)).findViewHolderForAdapterPosition(
|
||||
lastItemPosition);
|
||||
if (lastItemHolder != null) {
|
||||
lastItemHolder.addButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
|
||||
void showAlert(int mediaDetailDescription, int descriptionInfo);
|
||||
}
|
||||
|
||||
public interface EventListener {
|
||||
|
||||
void onPrimaryCaptionTextChange(boolean isNotEmpty);
|
||||
|
||||
void addLanguage();
|
||||
}
|
||||
|
||||
enum SelectedVoiceIcon {
|
||||
CAPTION,
|
||||
DESCRIPTION
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,563 @@
|
|||
package fr.free.nrw.commons.upload
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.speech.RecognizerIntent.ACTION_RECOGNIZE_SPEECH
|
||||
import android.speech.RecognizerIntent.EXTRA_LANGUAGE
|
||||
import android.speech.RecognizerIntent.EXTRA_LANGUAGE_MODEL
|
||||
import android.speech.RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
|
||||
import android.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.AdapterView.OnItemClickListener
|
||||
import android.widget.EditText
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ListView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.databinding.RowItemDescriptionBinding
|
||||
import fr.free.nrw.commons.recentlanguages.Language
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesAdapter
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesDao
|
||||
import fr.free.nrw.commons.utils.AbstractTextWatcher
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class UploadMediaDetailAdapter : RecyclerView.Adapter<UploadMediaDetailAdapter.ViewHolder> {
|
||||
private var uploadMediaDetails: MutableList<UploadMediaDetail>
|
||||
private var selectedLanguages: MutableMap<Int, String>
|
||||
private val savedLanguageValue: String
|
||||
private var recentLanguagesTextView: TextView? = null
|
||||
private var separator: View? = null
|
||||
private var languageHistoryListView: ListView? = null
|
||||
private var currentPosition = 0
|
||||
private var fragment: Fragment? = null
|
||||
private var activity: Activity? = null
|
||||
private val voiceInputResultLauncher: ActivityResultLauncher<Intent>
|
||||
private var selectedVoiceIcon: SelectedVoiceIcon? = null
|
||||
var recentLanguagesDao: RecentLanguagesDao
|
||||
var callback: Callback? = null
|
||||
var eventListener: EventListener? = null
|
||||
var items: List<UploadMediaDetail>
|
||||
get() = uploadMediaDetails
|
||||
set(value) {
|
||||
uploadMediaDetails = value.toMutableList()
|
||||
selectedLanguages = mutableMapOf()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
|
||||
constructor(
|
||||
fragment: Fragment?,
|
||||
savedLanguageValue: String,
|
||||
recentLanguagesDao: RecentLanguagesDao,
|
||||
voiceInputResultLauncher: ActivityResultLauncher<Intent>
|
||||
) {
|
||||
uploadMediaDetails = ArrayList()
|
||||
selectedLanguages = mutableMapOf()
|
||||
this.savedLanguageValue = savedLanguageValue
|
||||
this.recentLanguagesDao = recentLanguagesDao
|
||||
this.fragment = fragment
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher
|
||||
}
|
||||
|
||||
constructor(
|
||||
activity: Activity?,
|
||||
savedLanguageValue: String,
|
||||
uploadMediaDetails: MutableList<UploadMediaDetail>,
|
||||
recentLanguagesDao: RecentLanguagesDao,
|
||||
voiceInputResultLauncher: ActivityResultLauncher<Intent>
|
||||
) {
|
||||
this.uploadMediaDetails = uploadMediaDetails
|
||||
selectedLanguages = HashMap()
|
||||
this.savedLanguageValue = savedLanguageValue
|
||||
this.recentLanguagesDao = recentLanguagesDao
|
||||
this.activity = activity
|
||||
this.voiceInputResultLauncher = voiceInputResultLauncher
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return ViewHolder(RowItemDescriptionBinding.inflate(inflater, parent, false))
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a workaround for a known bug by android here
|
||||
* https://issuetracker.google.com/issues/37095917 makes the edit text on second and subsequent
|
||||
* fragments inside an adapter receptive to long click for copy/paste options
|
||||
*
|
||||
* @param holder the view holder
|
||||
*/
|
||||
override fun onViewAttachedToWindow(holder: ViewHolder) {
|
||||
super.onViewAttachedToWindow(holder)
|
||||
holder.binding.captionItemEditText.isEnabled = false
|
||||
holder.binding.captionItemEditText.isEnabled = true
|
||||
holder.binding.descriptionItemEditText.isEnabled = false
|
||||
holder.binding.descriptionItemEditText.isEnabled = true
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return uploadMediaDetails.size
|
||||
}
|
||||
|
||||
fun addDescription(uploadMediaDetail: UploadMediaDetail) {
|
||||
selectedLanguages[uploadMediaDetails.size] = "en"
|
||||
uploadMediaDetails.add(uploadMediaDetail)
|
||||
notifyItemInserted(uploadMediaDetails.size)
|
||||
}
|
||||
|
||||
private fun startSpeechInput(locale: String) {
|
||||
try {
|
||||
voiceInputResultLauncher.launch(Intent(ACTION_RECOGNIZE_SPEECH).apply {
|
||||
putExtra(EXTRA_LANGUAGE_MODEL, LANGUAGE_MODEL_FREE_FORM)
|
||||
putExtra(EXTRA_LANGUAGE, locale)
|
||||
})
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the result of the speech input by processing the spoken text.
|
||||
* If the spoken text is not empty, it capitalizes the first letter of the spoken text
|
||||
* and updates the appropriate field (caption or description) of the current
|
||||
* UploadMediaDetail based on the selected voice icon.
|
||||
* Finally, it notifies the adapter that the data set has changed.
|
||||
*
|
||||
* @param spokenText the text input received from speech recognition.
|
||||
*/
|
||||
fun handleSpeechResult(spokenText: String) {
|
||||
if (spokenText.isNotEmpty()) {
|
||||
val spokenTextCapitalized =
|
||||
spokenText.substring(0, 1).uppercase(Locale.getDefault()) + spokenText.substring(1)
|
||||
if (currentPosition < uploadMediaDetails.size) {
|
||||
val uploadMediaDetail = uploadMediaDetails[currentPosition]
|
||||
when (selectedVoiceIcon) {
|
||||
SelectedVoiceIcon.CAPTION -> uploadMediaDetail.captionText =
|
||||
spokenTextCapitalized
|
||||
|
||||
SelectedVoiceIcon.DESCRIPTION -> uploadMediaDetail.descriptionText =
|
||||
spokenTextCapitalized
|
||||
|
||||
null -> {}
|
||||
}
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove description based on position from the list and notifies the RecyclerView Adapter that
|
||||
* data in adapter has been removed at that particular position.
|
||||
*/
|
||||
fun removeDescription(uploadMediaDetail: UploadMediaDetail, position: Int) {
|
||||
selectedLanguages.remove(position)
|
||||
uploadMediaDetails.remove(uploadMediaDetail)
|
||||
var i = position + 1
|
||||
while (selectedLanguages.containsKey(i)) {
|
||||
selectedLanguages.remove(i)
|
||||
i++
|
||||
}
|
||||
notifyItemRemoved(position)
|
||||
notifyItemRangeChanged(position, uploadMediaDetails.size - position)
|
||||
updateAddButtonVisibility()
|
||||
}
|
||||
|
||||
inner class ViewHolder(val binding: RowItemDescriptionBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
var addButton: ImageView? = null
|
||||
|
||||
var clParent: ConstraintLayout? = null
|
||||
|
||||
var betterCaptionLinearLayout: LinearLayout? = null
|
||||
|
||||
var betterDescriptionLinearLayout: LinearLayout? = null
|
||||
|
||||
private var captionListener: AbstractTextWatcher? = null
|
||||
|
||||
var descriptionListener: AbstractTextWatcher? = null
|
||||
|
||||
fun bind(position: Int) {
|
||||
val uploadMediaDetail = uploadMediaDetails[position]
|
||||
Timber.d("UploadMediaDetail is %s", uploadMediaDetail)
|
||||
|
||||
addButton = binding.btnAdd
|
||||
clParent = binding.clParent
|
||||
betterCaptionLinearLayout = binding.llWriteBetterCaption
|
||||
betterDescriptionLinearLayout = binding.llWriteBetterDescription
|
||||
|
||||
|
||||
binding.descriptionLanguages.isFocusable = false
|
||||
binding.captionItemEditText.addTextChangedListener(AbstractTextWatcher { value: String ->
|
||||
if (position == 0) {
|
||||
eventListener!!.onPrimaryCaptionTextChange(value.length != 0)
|
||||
}
|
||||
})
|
||||
binding.captionItemEditText.removeTextChangedListener(captionListener)
|
||||
binding.descriptionItemEditText.removeTextChangedListener(descriptionListener)
|
||||
binding.captionItemEditText.setText(uploadMediaDetail.captionText)
|
||||
binding.descriptionItemEditText.setText(uploadMediaDetail.descriptionText)
|
||||
binding.captionItemEditTextInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
|
||||
binding.captionItemEditTextInputLayout.setEndIconDrawable(R.drawable.baseline_keyboard_voice)
|
||||
binding.captionItemEditTextInputLayout.setEndIconOnClickListener { v: View? ->
|
||||
currentPosition = position
|
||||
selectedVoiceIcon = SelectedVoiceIcon.CAPTION
|
||||
startSpeechInput(binding.descriptionLanguages.text.toString())
|
||||
}
|
||||
binding.descriptionItemEditTextInputLayout.endIconMode = TextInputLayout.END_ICON_CUSTOM
|
||||
binding.descriptionItemEditTextInputLayout.setEndIconDrawable(R.drawable.baseline_keyboard_voice)
|
||||
binding.descriptionItemEditTextInputLayout.setEndIconOnClickListener { v: View? ->
|
||||
currentPosition = position
|
||||
selectedVoiceIcon = SelectedVoiceIcon.DESCRIPTION
|
||||
startSpeechInput(binding.descriptionLanguages.text.toString())
|
||||
}
|
||||
|
||||
if (position == 0) {
|
||||
binding.btnRemove.visibility = View.GONE
|
||||
betterCaptionLinearLayout!!.visibility = View.VISIBLE
|
||||
betterCaptionLinearLayout!!.setOnClickListener { v: View? ->
|
||||
callback!!.showAlert(
|
||||
R.string.media_detail_caption,
|
||||
R.string.caption_info
|
||||
)
|
||||
}
|
||||
betterDescriptionLinearLayout!!.visibility = View.VISIBLE
|
||||
betterDescriptionLinearLayout!!.setOnClickListener { v: View? ->
|
||||
callback!!.showAlert(
|
||||
R.string.media_detail_description,
|
||||
R.string.description_info
|
||||
)
|
||||
}
|
||||
|
||||
binding.captionItemEditTextInputLayout.editText?.let {
|
||||
it.filters = arrayOf<InputFilter>(UploadMediaDetailInputFilter())
|
||||
}
|
||||
} else {
|
||||
binding.btnRemove.visibility = View.VISIBLE
|
||||
betterCaptionLinearLayout!!.visibility = View.GONE
|
||||
betterDescriptionLinearLayout!!.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.btnRemove.setOnClickListener { v: View? ->
|
||||
removeDescription(
|
||||
uploadMediaDetail,
|
||||
position
|
||||
)
|
||||
}
|
||||
captionListener = AbstractTextWatcher { captionText: String ->
|
||||
uploadMediaDetail.captionText =
|
||||
convertIdeographicSpaceToLatinSpace(captionText.trim())
|
||||
}
|
||||
descriptionListener = AbstractTextWatcher { value: String? ->
|
||||
uploadMediaDetail.descriptionText = value
|
||||
}
|
||||
binding.captionItemEditText.addTextChangedListener(captionListener)
|
||||
initLanguage(position, uploadMediaDetail)
|
||||
|
||||
binding.descriptionItemEditText.addTextChangedListener(descriptionListener)
|
||||
initLanguage(position, uploadMediaDetail)
|
||||
|
||||
if (fragment != null) {
|
||||
val newLayoutParams = clParent!!.layoutParams as FrameLayout.LayoutParams
|
||||
newLayoutParams.topMargin = 0
|
||||
newLayoutParams.leftMargin = 0
|
||||
newLayoutParams.rightMargin = 0
|
||||
newLayoutParams.bottomMargin = 0
|
||||
clParent!!.layoutParams = newLayoutParams
|
||||
}
|
||||
updateAddButtonVisibility()
|
||||
addButton!!.setOnClickListener { v: View? -> eventListener!!.addLanguage() }
|
||||
|
||||
//If the description was manually added by the user, it deserves focus, if not, let the user decide
|
||||
if (uploadMediaDetail.isManuallyAdded) {
|
||||
binding.captionItemEditText.requestFocus()
|
||||
} else {
|
||||
binding.captionItemEditText.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun initLanguage(position: Int, description: UploadMediaDetail) {
|
||||
val recentLanguages = recentLanguagesDao.getRecentLanguages()
|
||||
|
||||
val languagesAdapter = LanguagesAdapter(
|
||||
binding.descriptionLanguages.context,
|
||||
selectedLanguages
|
||||
)
|
||||
|
||||
binding.descriptionLanguages.setOnClickListener { view ->
|
||||
val dialog = Dialog(view.context)
|
||||
dialog.setContentView(R.layout.dialog_select_language)
|
||||
dialog.setCancelable(false)
|
||||
dialog.window!!.setLayout(
|
||||
(view.context.resources.displayMetrics.widthPixels
|
||||
* 0.90).toInt(),
|
||||
(view.context.resources.displayMetrics.heightPixels
|
||||
* 0.90).toInt()
|
||||
)
|
||||
dialog.show()
|
||||
|
||||
val editText =
|
||||
dialog.findViewById<EditText>(R.id.search_language)
|
||||
val listView =
|
||||
dialog.findViewById<ListView>(R.id.language_list)
|
||||
languageHistoryListView =
|
||||
dialog.findViewById(R.id.language_history_list)
|
||||
recentLanguagesTextView =
|
||||
dialog.findViewById(R.id.recent_searches)
|
||||
separator =
|
||||
dialog.findViewById(R.id.separator)
|
||||
setUpRecentLanguagesSection(recentLanguages)
|
||||
|
||||
listView.adapter = languagesAdapter
|
||||
|
||||
editText.addTextChangedListener(object : TextWatcher {
|
||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) =
|
||||
hideRecentLanguagesSection()
|
||||
|
||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
|
||||
languagesAdapter.filter.filter(charSequence)
|
||||
}
|
||||
|
||||
override fun afterTextChanged(editable: Editable) = Unit
|
||||
})
|
||||
|
||||
languageHistoryListView?.setOnItemClickListener { adapterView: AdapterView<*>, view1: View?, position: Int, id: Long ->
|
||||
onRecentLanguageClicked(dialog, adapterView, position, description)
|
||||
}
|
||||
|
||||
listView.onItemClickListener = OnItemClickListener { adapterView, _, i, l ->
|
||||
description.selectedLanguageIndex = i
|
||||
val languageCode = (adapterView.adapter as LanguagesAdapter).getLanguageCode(i)
|
||||
description.languageCode = languageCode
|
||||
val languageName = (adapterView.adapter as LanguagesAdapter).getLanguageName(i)
|
||||
val isExists = recentLanguagesDao.findRecentLanguage(languageCode)
|
||||
if (isExists) {
|
||||
recentLanguagesDao.deleteRecentLanguage(languageCode)
|
||||
}
|
||||
recentLanguagesDao.addRecentLanguage(Language(languageName, languageCode))
|
||||
|
||||
selectedLanguages.clear()
|
||||
selectedLanguages[position] = languageCode
|
||||
(adapterView.adapter as LanguagesAdapter).selectedLangCode = languageCode
|
||||
Timber.d("Description language code is: %s", languageCode)
|
||||
binding.descriptionLanguages.text = languageCode
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
languagesAdapter.filter.filter("")
|
||||
}
|
||||
}
|
||||
|
||||
if (description.selectedLanguageIndex == -1) {
|
||||
if (!TextUtils.isEmpty(savedLanguageValue)) {
|
||||
// If user has chosen a default language from settings activity
|
||||
// savedLanguageValue is not null
|
||||
if (!TextUtils.isEmpty(description.languageCode)) {
|
||||
binding.descriptionLanguages.text = description.languageCode
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] = description.languageCode!!
|
||||
} else {
|
||||
description.languageCode = savedLanguageValue
|
||||
binding.descriptionLanguages.text = savedLanguageValue
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] = savedLanguageValue
|
||||
}
|
||||
} else if (!TextUtils.isEmpty(description.languageCode)) {
|
||||
binding.descriptionLanguages.text = description.languageCode
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] = description.languageCode!!
|
||||
} else {
|
||||
//Checking whether Language Code attribute is null or not.
|
||||
if (uploadMediaDetails[position].languageCode != 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.
|
||||
binding.descriptionLanguages.text = uploadMediaDetails[position]
|
||||
.languageCode
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] = uploadMediaDetails[position].languageCode!!
|
||||
} else {
|
||||
if (position == 0) {
|
||||
val defaultLocaleIndex = languagesAdapter.getIndexOfUserDefaultLocale(
|
||||
binding.descriptionLanguages.getContext())
|
||||
binding.descriptionLanguages.setText(languagesAdapter.getLanguageCode(defaultLocaleIndex))
|
||||
description.languageCode = languagesAdapter.getLanguageCode(defaultLocaleIndex)
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] =
|
||||
languagesAdapter.getLanguageCode(defaultLocaleIndex)
|
||||
} else {
|
||||
description.languageCode = languagesAdapter.getLanguageCode(0)
|
||||
binding.descriptionLanguages.text = languagesAdapter.getLanguageCode(0)
|
||||
selectedLanguages.remove(position)
|
||||
selectedLanguages[position] = languagesAdapter.getLanguageCode(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.descriptionLanguages.text = description.languageCode
|
||||
selectedLanguages.remove(position)
|
||||
description.languageCode?.let {
|
||||
selectedLanguages[position] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles click event for recent language section
|
||||
*/
|
||||
private fun onRecentLanguageClicked(
|
||||
dialog: Dialog, adapterView: AdapterView<*>,
|
||||
position: Int, description: UploadMediaDetail
|
||||
) {
|
||||
description.selectedLanguageIndex = position
|
||||
val languageCode = (adapterView.adapter as RecentLanguagesAdapter)
|
||||
.getLanguageCode(position)
|
||||
description.languageCode = languageCode
|
||||
val languageName = (adapterView.adapter as RecentLanguagesAdapter)
|
||||
.getLanguageName(position)
|
||||
val isExists = recentLanguagesDao.findRecentLanguage(languageCode)
|
||||
if (isExists) {
|
||||
recentLanguagesDao.deleteRecentLanguage(languageCode)
|
||||
}
|
||||
recentLanguagesDao.addRecentLanguage(Language(languageName, languageCode))
|
||||
|
||||
selectedLanguages.clear()
|
||||
selectedLanguages[position] = languageCode
|
||||
(adapterView
|
||||
.adapter as RecentLanguagesAdapter).selectedLangCode = languageCode
|
||||
Timber.d("Description language code is: %s", languageCode)
|
||||
binding.descriptionLanguages.text = languageCode
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides recent languages section
|
||||
*/
|
||||
private fun hideRecentLanguagesSection() {
|
||||
languageHistoryListView!!.visibility = View.GONE
|
||||
recentLanguagesTextView!!.visibility = View.GONE
|
||||
separator!!.visibility = View.GONE
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up recent languages section
|
||||
*
|
||||
* @param recentLanguages recently used languages
|
||||
*/
|
||||
private fun setUpRecentLanguagesSection(recentLanguages: List<Language>) {
|
||||
if (recentLanguages.isEmpty()) {
|
||||
languageHistoryListView!!.visibility = View.GONE
|
||||
recentLanguagesTextView!!.visibility = View.GONE
|
||||
separator!!.visibility = View.GONE
|
||||
} else {
|
||||
if (recentLanguages.size > 5) {
|
||||
for (i in recentLanguages.size - 1 downTo 5) {
|
||||
recentLanguagesDao.deleteRecentLanguage(
|
||||
recentLanguages[i]
|
||||
.languageCode
|
||||
)
|
||||
}
|
||||
}
|
||||
languageHistoryListView!!.visibility = View.VISIBLE
|
||||
recentLanguagesTextView!!.visibility = View.VISIBLE
|
||||
separator!!.visibility = View.VISIBLE
|
||||
|
||||
val recentLanguagesAdapter = RecentLanguagesAdapter(
|
||||
binding.descriptionLanguages.context,
|
||||
recentLanguagesDao.getRecentLanguages(),
|
||||
selectedLanguages
|
||||
)
|
||||
languageHistoryListView!!.adapter = recentLanguagesAdapter
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Ideographic space to Latin space
|
||||
*
|
||||
* @param source the source text
|
||||
* @return a string with Latin spaces instead of Ideographic spaces
|
||||
*/
|
||||
fun convertIdeographicSpaceToLatinSpace(source: String): String {
|
||||
val ideographicSpacePattern = Pattern.compile("\\x{3000}")
|
||||
return ideographicSpacePattern.matcher(source).replaceAll(" ")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the visibility of the "Add" button for all items in the RecyclerView except
|
||||
* the last item in RecyclerView
|
||||
*/
|
||||
private fun updateAddButtonVisibility() {
|
||||
val lastItemPosition = itemCount - 1
|
||||
// Hide Add Button for all items
|
||||
for (i in 0 until itemCount) {
|
||||
if (fragment != null) {
|
||||
if (fragment!!.view != null) {
|
||||
val holder = (fragment!!.requireView().findViewById<View>(R.id.rv_descriptions) as RecyclerView).findViewHolderForAdapterPosition(i) as ViewHolder?
|
||||
if (holder != null) {
|
||||
holder.addButton!!.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (activity != null) {
|
||||
val holder = (activity!!.findViewById<View>(R.id.rv_descriptions_captions) as RecyclerView).findViewHolderForAdapterPosition(i) as ViewHolder?
|
||||
if (holder != null) {
|
||||
holder.addButton!!.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show Add Button for the last item
|
||||
if (fragment != null) {
|
||||
if (fragment!!.view != null) {
|
||||
val lastItemHolder = (fragment!!.requireView().findViewById<View>(R.id.rv_descriptions) as RecyclerView).findViewHolderForAdapterPosition(lastItemPosition) as ViewHolder?
|
||||
if (lastItemHolder != null) {
|
||||
lastItemHolder.addButton!!.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (activity != null) {
|
||||
val lastItemHolder = (activity!!.findViewById<View>(R.id.rv_descriptions_captions) as RecyclerView).findViewHolderForAdapterPosition(lastItemPosition) as ViewHolder?
|
||||
if (lastItemHolder != null) {
|
||||
lastItemHolder.addButton!!.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface Callback {
|
||||
fun showAlert(mediaDetailDescription: Int, descriptionInfo: Int)
|
||||
}
|
||||
|
||||
interface EventListener {
|
||||
fun onPrimaryCaptionTextChange(isNotEmpty: Boolean)
|
||||
fun addLanguage()
|
||||
}
|
||||
|
||||
internal enum class SelectedVoiceIcon {
|
||||
CAPTION,
|
||||
DESCRIPTION
|
||||
}
|
||||
}
|
||||
|
|
@ -223,7 +223,7 @@ class SettingsFragmentUnitTests {
|
|||
RecentLanguagesAdapter(
|
||||
context,
|
||||
listOf(Language("English", "en")),
|
||||
hashMapOf<String, String>(),
|
||||
mutableMapOf(),
|
||||
),
|
||||
)
|
||||
val method: Method =
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class LanguagesAdapterTest {
|
|||
private lateinit var context: Context
|
||||
|
||||
@Mock
|
||||
private lateinit var selectedLanguages: HashMap<Integer, String>
|
||||
private lateinit var selectedLanguages: MutableMap<Int, String>
|
||||
|
||||
@Mock
|
||||
private lateinit var parent: ViewGroup
|
||||
|
|
@ -41,7 +41,7 @@ class LanguagesAdapterTest {
|
|||
|
||||
private lateinit var languagesAdapter: LanguagesAdapter
|
||||
private lateinit var convertView: View
|
||||
private var selectLanguages: HashMap<Integer, String> = HashMap()
|
||||
private var selectLanguages: MutableMap<Int, String> = mutableMapOf()
|
||||
|
||||
@Before
|
||||
@Throws(Exception::class)
|
||||
|
|
@ -94,8 +94,8 @@ class LanguagesAdapterTest {
|
|||
|
||||
@Test
|
||||
fun testSelectLanguageNotEmpty() {
|
||||
selectLanguages[Integer(0)] = "es"
|
||||
selectLanguages[Integer(1)] = "de"
|
||||
selectLanguages[0] = "es"
|
||||
selectLanguages[1] = "de"
|
||||
languagesAdapter = LanguagesAdapter(context, selectLanguages)
|
||||
|
||||
Assertions.assertEquals(false, languagesAdapter.isEnabled(languagesAdapter.getIndexOfLanguageCode("es")))
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ class UploadMediaDetailAdapterUnitTest {
|
|||
RecentLanguagesAdapter(
|
||||
context,
|
||||
listOf(Language("English", "en")),
|
||||
hashMapOf<String, String>(),
|
||||
mutableMapOf(),
|
||||
),
|
||||
)
|
||||
val method: Method =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue