package de.westnordost.streetcomplete.quests.road_name;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import java.util.Locale;
import javax.inject.Inject;
import dagger.Lazy;
import de.westnordost.streetcomplete.Injector;
import de.westnordost.streetcomplete.data.meta.Abbreviations;
import de.westnordost.streetcomplete.data.meta.CurrentCountry;
/** An edit text that expands abbreviations automatically when finishing a word (via space, "-" or
* ".") and capitalizes the first letter of each word that is longer than 3 letters. */
public class AutoCorrectAbbreviationsEditText extends EditText
{
// Creation of Abbreviations takes some time, let's defer it until after the injection (so that
// we can load it asynchronously
@Inject Lazy<Abbreviations> abbreviations;
@Inject CurrentCountry currentCountry;
public AutoCorrectAbbreviationsEditText(Context context)
{
super(context);
init();
}
public AutoCorrectAbbreviationsEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
init();
}
public AutoCorrectAbbreviationsEditText(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
init();
}
private void init()
{
Injector.instance.getApplicationComponent().inject(this);
// asynchronously load the abbreviations already because we will need it latest after the
// user wrote the first word (in debug mode, it takes whopping 3 seconds on my phone :-( )
new Thread()
{
@Override public void run()
{
abbreviations.get();
}
}.start();
setImeOptions(EditorInfo.IME_ACTION_DONE | getImeOptions());
addTextChangedListener(new AbbreviationAutoCorrecter());
setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS |
EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES);
setOnEditorActionListener(new TextView.OnEditorActionListener()
{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
if (actionId == EditorInfo.IME_ACTION_DONE)
{
autoCorrectTextAt(getText(), length());
}
return false;
}
});
}
private void autoCorrectTextAt(Editable s, int cursor)
{
String textToCursor = s.subSequence(0, cursor).toString();
String[] words = textToCursor.split("[ -]+");
String lastWordBeforeCursor = words[words.length-1];
boolean isFirstWord = words.length == 1;
boolean isLastWord = cursor == s.length();
String replacement = abbreviations.get().getExpansion(lastWordBeforeCursor, isFirstWord, isLastWord);
int wordStart = textToCursor.indexOf(lastWordBeforeCursor);
if(replacement != null)
{
fixedReplace(s, wordStart, wordStart + lastWordBeforeCursor.length(), replacement);
}
else if (lastWordBeforeCursor.length() > 3)
{
Locale locale = currentCountry.getLocale();
String capital = lastWordBeforeCursor.substring(0, 1).toUpperCase(locale);
s.replace(wordStart, wordStart + 1, capital);
}
}
private void fixedReplace(Editable s, int replaceStart, int replaceEnd, CharSequence replaceWith)
{
// if I only call s.replace to replace the abbreviations with their respective expansions,
// the caret seems to get confused. On my Android API level 19, if i.e. an abbreviation of
// two letters is replaced with a word with ten letters, then I can not edit/delete the first
// eight letters of the edit text anymore.
// This method re-sets the text completely, so the caret and text length are also set anew
int selEnd = getSelectionEnd();
setText(s.replace(replaceStart, replaceEnd, replaceWith));
int replaceLength = replaceEnd - replaceStart;
int addedCharacters = replaceWith.length() - replaceLength;
setSelection(selEnd + addedCharacters);
}
public boolean containsAbbreviations()
{
return abbreviations.get().containsAbbreviations(getText().toString());
}
private class AbbreviationAutoCorrecter implements TextWatcher
{
private int cursorPos;
private int lastTextLength;
private boolean addedText;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
cursorPos = start + count;
addedText = lastTextLength < s.length();
lastTextLength = s.length();
}
@Override
public void afterTextChanged(Editable s)
{
if(addedText)
{
boolean endedWord = s.subSequence(0, cursorPos).toString().matches(".+[ -]+$");
if (endedWord)
{
autoCorrectTextAt(s, cursorPos);
}
}
}
}
}