mirror of
https://github.com/Helium314/HeliBoard.git
synced 2025-05-19 08:20:15 +00:00
allow removing suggestions
This commit is contained in:
parent
cdabf650c6
commit
601b994941
7 changed files with 209 additions and 36 deletions
|
@ -396,16 +396,6 @@ public class KeyboardView extends View {
|
|||
bgX = -padding.left;
|
||||
bgY = -padding.top;
|
||||
}
|
||||
/* if (mUserTheme) {
|
||||
// color filter is applied to background, which is re-used
|
||||
// but we don't want it applied to "blue" keys
|
||||
// so we always need to select the color filter dependent on the current key
|
||||
if (key.isActionKey()
|
||||
|| (key.getBackgroundType() == Key.BACKGROUND_TYPE_NORMAL && key.getCode() < 0 && key.getCode() != Constants.CODE_SWITCH_ALPHA_SYMBOL))
|
||||
background.clearColorFilter();
|
||||
else
|
||||
background.setColorFilter(keyBgFilter);
|
||||
}*/
|
||||
if (mUserTheme) {
|
||||
// color filter is applied to background, which is re-used
|
||||
// but we don't want it applied to "blue" keys
|
||||
|
|
|
@ -121,6 +121,8 @@ public interface DictionaryFacilitator {
|
|||
final String dictNamePrefix,
|
||||
@Nullable final DictionaryInitializationListener listener);
|
||||
|
||||
void removeWord(String word);
|
||||
|
||||
@UsedForTesting
|
||||
void resetDictionariesForTesting(
|
||||
final Context context,
|
||||
|
|
|
@ -38,8 +38,11 @@ import org.dslul.openboard.inputmethod.latin.utils.ScriptUtils;
|
|||
import org.dslul.openboard.inputmethod.latin.utils.SuggestionResults;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -47,6 +50,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -155,6 +160,10 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
// in this language.
|
||||
private int mConfidence = 1;
|
||||
|
||||
// words cannot be removed from main dictionary, so we use a blacklist instead
|
||||
public String blacklistFileName = null;
|
||||
public Set<String> blacklist = new HashSet<>();
|
||||
|
||||
// allow to go above max confidence, for better determination of currently preferred language
|
||||
// when decreasing confidence or getting weight factor, limit to maximum
|
||||
public void increaseConfidence() {
|
||||
|
@ -455,6 +464,20 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
mSecondaryDictionaryGroup == null ? null : secondaryLocale, listener);
|
||||
}
|
||||
}
|
||||
|
||||
// load blacklists
|
||||
mDictionaryGroup.blacklistFileName = context.getFilesDir().getAbsolutePath() + File.separator + "blacklists" + File.separator + newLocale.toString().toLowerCase(Locale.ENGLISH) + ".txt";
|
||||
if (!new File(mDictionaryGroup.blacklistFileName).exists())
|
||||
new File(context.getFilesDir().getAbsolutePath() + File.separator + "blacklists").mkdirs();
|
||||
mDictionaryGroup.blacklist.addAll(readBlacklistFile(mDictionaryGroup.blacklistFileName));
|
||||
|
||||
if (mSecondaryDictionaryGroup != null) {
|
||||
mSecondaryDictionaryGroup.blacklistFileName = context.getFilesDir().getAbsolutePath() + File.separator + "blacklists" + File.separator + secondaryLocale.toString().toLowerCase(Locale.ENGLISH) + ".txt";
|
||||
if (!new File(mSecondaryDictionaryGroup.blacklistFileName).exists())
|
||||
new File(context.getFilesDir().getAbsolutePath() + File.separator + "blacklists").mkdirs();
|
||||
mSecondaryDictionaryGroup.blacklist.addAll(readBlacklistFile(mSecondaryDictionaryGroup.blacklistFileName));
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary());
|
||||
}
|
||||
|
@ -655,6 +678,12 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
blockPotentiallyOffensive);
|
||||
ngramContextForCurrentWord =
|
||||
ngramContextForCurrentWord.getNextNgramContext(new WordInfo(currentWord));
|
||||
|
||||
// remove entered words from blacklist
|
||||
if (mDictionaryGroup.blacklist.remove(currentWord))
|
||||
removeWordFromBlacklistFile(currentWord, mDictionaryGroup.blacklistFileName);
|
||||
if (mSecondaryDictionaryGroup != null && mSecondaryDictionaryGroup.blacklist.remove(currentWord))
|
||||
removeWordFromBlacklistFile(currentWord, mSecondaryDictionaryGroup.blacklistFileName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,7 +863,14 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
proximityInfoHandle, settingsValuesForSuggestion, sessionId,
|
||||
weightForLocale, weightOfLangModelVsSpatialModel);
|
||||
if (null == dictionarySuggestions) continue;
|
||||
suggestions.addAll(dictionarySuggestions);
|
||||
|
||||
// don't add blacklisted words
|
||||
// this may not be the most efficient way, but getting suggestions is much slower anyway
|
||||
for (SuggestedWordInfo info : dictionarySuggestions) {
|
||||
if (!isBlacklisted(info.getWord())) {
|
||||
suggestions.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
return suggestions;
|
||||
}
|
||||
|
@ -865,6 +901,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
if (dictionaryGroup.mLocale == null) {
|
||||
return false;
|
||||
}
|
||||
if (isBlacklisted(word)) return false;
|
||||
for (final String dictType : dictionariesToCheck) {
|
||||
final Dictionary dictionary = dictionaryGroup.getDict(dictType);
|
||||
// Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
|
||||
|
@ -878,6 +915,87 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlacklisted(final String word) {
|
||||
if (mDictionaryGroup.blacklist.contains(word))
|
||||
return true;
|
||||
if (mSecondaryDictionaryGroup != null && mSecondaryDictionaryGroup.blacklist.contains(word))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeWord(String word) {
|
||||
removeWordFromGroup(word, mDictionaryGroup);
|
||||
if (mSecondaryDictionaryGroup != null)
|
||||
removeWordFromGroup(word, mSecondaryDictionaryGroup);
|
||||
}
|
||||
|
||||
private void removeWordFromGroup(String word, DictionaryGroup group) {
|
||||
// remove from user history
|
||||
final ExpandableBinaryDictionary historyDict = group.getSubDict(Dictionary.TYPE_USER_HISTORY);
|
||||
if (historyDict != null) {
|
||||
historyDict.removeUnigramEntryDynamically(word);
|
||||
}
|
||||
// and from personal dictionary
|
||||
final ExpandableBinaryDictionary userDict = group.getSubDict(Dictionary.TYPE_USER);
|
||||
if (userDict != null) {
|
||||
userDict.removeUnigramEntryDynamically(word);
|
||||
}
|
||||
|
||||
// add to blacklist if in main dictionary
|
||||
if (group.getDict(Dictionary.TYPE_MAIN).isValidWord(word) && group.blacklist.add(word)) {
|
||||
// write to file if word wasn't already in blacklist
|
||||
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream(group.blacklistFileName, true);
|
||||
fos.write((word + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while trying to write blacklist", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<String> readBlacklistFile(final String filename) {
|
||||
final ArrayList<String> blacklist = new ArrayList<>();
|
||||
if (filename == null) return blacklist;
|
||||
File blacklistFile = new File(filename);
|
||||
if (!blacklistFile.exists()) return blacklist;
|
||||
try {
|
||||
final Scanner scanner = new Scanner(blacklistFile, StandardCharsets.UTF_8.name()).useDelimiter("\n");
|
||||
while (scanner.hasNext()) {
|
||||
blacklist.add(scanner.next());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while reading blacklist", e);
|
||||
}
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
private void removeWordFromBlacklistFile(String word, String filename) {
|
||||
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.KEYBOARD).execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
ArrayList<String> blacklist = readBlacklistFile(filename);
|
||||
blacklist.remove(word);
|
||||
FileOutputStream fos = new FileOutputStream(filename);
|
||||
for (String entry : blacklist) {
|
||||
fos.write((entry + "\n").getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception while trying to write blacklist" + filename, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// called from addWordToUserHistory with a specified dictionary, so provide this dictionary
|
||||
private int getFrequency(final String word, DictionaryGroup dictGroup) {
|
||||
if (TextUtils.isEmpty(word)) {
|
||||
|
|
|
@ -1684,6 +1684,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
setSuggestedWords(neutralSuggestions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSuggestion(final String word) {
|
||||
mDictionaryFacilitator.removeWord(word);
|
||||
}
|
||||
|
||||
// Outside LatinIME, only used by the {@link InputTestsBase} test suite.
|
||||
@UsedForTesting
|
||||
void loadKeyboard() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.util.AttributeSet;
|
|||
import android.util.TypedValue;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
@ -35,6 +36,7 @@ import android.view.ViewGroup;
|
|||
import android.view.ViewParent;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -51,6 +53,7 @@ import org.dslul.openboard.inputmethod.latin.define.DebugFlags;
|
|||
import org.dslul.openboard.inputmethod.latin.settings.Settings;
|
||||
import org.dslul.openboard.inputmethod.latin.settings.SettingsValues;
|
||||
import org.dslul.openboard.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener;
|
||||
import org.dslul.openboard.inputmethod.latin.utils.DialogUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -62,6 +65,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
|
|||
void pickSuggestionManually(SuggestedWordInfo word);
|
||||
void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat);
|
||||
void onTextInput(final String rawText);
|
||||
void removeSuggestion(final String word);
|
||||
CharSequence getSelection();
|
||||
}
|
||||
|
||||
|
@ -290,7 +294,57 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
|
|||
}
|
||||
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
|
||||
Constants.NOT_A_CODE, this);
|
||||
return showMoreSuggestions();
|
||||
if (isShowingMoreSuggestionPanel() || !showMoreSuggestions()) {
|
||||
for (int i = 0; i < mStartIndexOfMoreSuggestions; i++) {
|
||||
if (view == mWordViews.get(i)) {
|
||||
showDeleteSuggestionDialog(mWordViews.get(i));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showDeleteSuggestionDialog(final TextView wordView) {
|
||||
final String word = wordView.getText().toString();
|
||||
|
||||
final PopupMenu menu = new PopupMenu(DialogUtils.getPlatformDialogThemeContext(getContext()), wordView);
|
||||
menu.getMenu().add(R.string.remove_suggestions);
|
||||
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem menuItem) {
|
||||
mListener.removeSuggestion(word);
|
||||
mMoreSuggestionsView.dismissMoreKeysPanel();
|
||||
// show suggestions, but without the removed word
|
||||
final ArrayList<SuggestedWordInfo> sw = new ArrayList<SuggestedWordInfo>();
|
||||
for (int i = 0; i < mSuggestedWords.size(); i ++) {
|
||||
final SuggestedWordInfo info = mSuggestedWords.getInfo(i);
|
||||
if (!info.getWord().equals(word))
|
||||
sw.add(info);
|
||||
}
|
||||
ArrayList<SuggestedWordInfo> rs = null;
|
||||
if (mSuggestedWords.mRawSuggestions != null) {
|
||||
rs = mSuggestedWords.mRawSuggestions;
|
||||
for (int i = 0; i < rs.size(); i ++) {
|
||||
if (rs.get(i).getWord().equals(word)) {
|
||||
rs.remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// copied code from setSuggestions, but without the Rtl part
|
||||
clear();
|
||||
mSuggestedWords = new SuggestedWords(sw, rs, mSuggestedWords.getTypedWordInfo(),
|
||||
mSuggestedWords.mTypedWordValid, mSuggestedWords.mWillAutoCorrect,
|
||||
mSuggestedWords.mIsObsoleteSuggestions, mSuggestedWords.mInputStyle,
|
||||
mSuggestedWords.mSequenceNumber);
|
||||
mStartIndexOfMoreSuggestions = mLayoutHelper.layoutAndReturnStartIndexOfMoreSuggestions(
|
||||
getContext(), mSuggestedWords, mSuggestionsStrip, SuggestionStripView.this);
|
||||
mStripVisibilityGroup.showSuggestionsStrip();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
menu.show();
|
||||
}
|
||||
|
||||
boolean showMoreSuggestions() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue