1
0
Fork 0

added a new screen, in the Settings, for deleting added words

This commit is contained in:
sspanak 2024-03-07 11:52:18 +02:00 committed by Dimo Karaivanov
parent 5a43fba3d5
commit 322774ab45
30 changed files with 477 additions and 26 deletions

View file

@ -170,8 +170,8 @@ public class DictionaryLoader {
logLoadingStep("Indexes dropped", language, start);
start = System.currentTimeMillis();
DeleteOps.delete(sqlite, language.getId());
DeleteOps.delete(sqlite, new EmojiLanguage().getId());
DeleteOps.delete(sqlite.getDb(), language.getId());
DeleteOps.delete(sqlite.getDb(), new EmojiLanguage().getId());
sendProgressMessage(language, ++progress, SettingsStore.DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME);
logLoadingStep("Storage cleared", language, start);

View file

@ -62,7 +62,7 @@ public class WordStore {
return new ArrayList<>();
}
if (sequence == null || sequence.length() == 0) {
if (sequence == null || sequence.isEmpty()) {
Logger.w(LOG_TAG, "Attempting to get words for an empty sequence.");
return new ArrayList<>();
}
@ -91,6 +91,11 @@ public class WordStore {
}
@NonNull public ArrayList<String> getSimilarCustom(Language language, String wordFilter) {
return language != null && checkOrNotify() ? readOps.getCustomWords(sqlite.getDb(), language, wordFilter) : new ArrayList<>();
}
@NonNull public String getLanguageFileHash(Language language) {
return language != null && checkOrNotify() ? readOps.getLanguageFileHash(sqlite.getDb(), language.getId()) : "";
}
@ -111,7 +116,7 @@ public class WordStore {
sqlite.beginTransaction();
for (int langId : languageIds) {
if (readOps.exists(sqlite.getDb(), langId)) {
DeleteOps.delete(sqlite, langId);
DeleteOps.delete(sqlite.getDb(), langId);
}
}
sqlite.finishTransaction();
@ -124,6 +129,23 @@ public class WordStore {
}
public void removeCustomWord(Language language, String word) {
if (language == null || !checkOrNotify()) {
return;
}
try {
sqlite.beginTransaction();
DeleteOps.deleteCustomWord(sqlite.getDb(), language.getId(), word);
sqlite.finishTransaction();
} catch (Exception e) {
sqlite.failTransaction();
Logger.e(LOG_TAG, "Failed deleting custom word: '" + word + "' for language: " + language.getId() + ". " + e.getMessage());
}
}
public int put(Language language, String word) {
if (word == null || word.isEmpty()) {
return AddWordDialog.CODE_BLANK_WORD;

View file

@ -35,15 +35,19 @@ public class WordStoreAsync {
}
public static void areThereWords(ConsumerCompat<Boolean> notification, Language language) {
new Thread(() -> notification.accept(getStore().exists(language))).start();
}
public static void getLastLanguageUpdateTime(ConsumerCompat<String> notification, Language language) {
new Thread(() -> notification.accept(getStore().getLanguageFileHash(language))).start();
}
public static void deleteCustomWord(Runnable notification, Language language, String word) {
new Thread(() -> {
getStore().removeCustomWord(language, word);
notification.run();
}).start();
}
public static void deleteWords(Runnable notification, @NonNull ArrayList<Integer> languageIds) {
new Thread(() -> {
getStore().remove(languageIds);
@ -67,4 +71,11 @@ public class WordStoreAsync {
getStore().getSimilar(language, sequence, filter, minWords, maxWords)))
).start();
}
public static void getCustomWords(ConsumerCompat<ArrayList<String>> dataHandler, Language language, String wordFilter) {
new Thread(() -> asyncHandler.post(() -> dataHandler.accept(
getStore().getSimilarCustom(language, wordFilter)))
).start();
}
}

View file

@ -1,10 +1,17 @@
package io.github.sspanak.tt9.db.sqlite;
import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.NonNull;
public class DeleteOps {
public static void delete(@NonNull SQLiteOpener sqlite, int languageId) {
sqlite.getDb().delete(Tables.getWords(languageId), null, null);
sqlite.getDb().delete(Tables.getWordPositions(languageId), null, null);
public static void delete(@NonNull SQLiteDatabase db, int languageId) {
db.delete(Tables.getWords(languageId), null, null);
db.delete(Tables.getWordPositions(languageId), null, null);
}
public static void deleteCustomWord(@NonNull SQLiteDatabase db, int languageId, String word) {
db.delete(Tables.getWords(languageId), "word = ?", new String[] { word });
db.delete(Tables.CUSTOM_WORDS, "word = ?", new String[] { word });
}
}

View file

@ -7,10 +7,13 @@ import android.database.sqlite.SQLiteStatement;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.db.SlowQueryStats;
import io.github.sspanak.tt9.db.entities.WordList;
import io.github.sspanak.tt9.db.entities.WordPositionsStringBuilder;
import io.github.sspanak.tt9.languages.EmojiLanguage;
import io.github.sspanak.tt9.languages.Language;
public class ReadOps {
@ -62,6 +65,27 @@ public class ReadOps {
}
public ArrayList<String> getCustomWords(@NonNull SQLiteDatabase db, @NonNull Language language, @NonNull String wordFilter) {
ArrayList<String> words = new ArrayList<>();
String[] select = new String[]{"word"};
String where = "word LIKE ? AND (langId = ? OR langId = ?)";
String[] whereArgs = new String[] {
wordFilter + "%",
String.valueOf(language.getId()),
String.valueOf(new EmojiLanguage().getId())
};
try (Cursor cursor = db.query(Tables.CUSTOM_WORDS, select, where, whereArgs, null, null, "word")) {
while (cursor.moveToNext()) {
words.add(cursor.getString(0));
}
}
return words;
}
/**
* Gets all words as a ready-to-export CSV string. If the language is null or customWords is true,
* only custom words are returned.

View file

@ -25,6 +25,7 @@ import io.github.sspanak.tt9.preferences.screens.MainSettingsScreen;
import io.github.sspanak.tt9.preferences.screens.UsageStatsScreen;
import io.github.sspanak.tt9.preferences.screens.appearance.AppearanceScreen;
import io.github.sspanak.tt9.preferences.screens.debug.DebugScreen;
import io.github.sspanak.tt9.preferences.screens.deleteWords.DeleteWordsScreen;
import io.github.sspanak.tt9.preferences.screens.hotkeys.HotkeysScreen;
import io.github.sspanak.tt9.preferences.screens.keypad.KeyPadScreen;
import io.github.sspanak.tt9.preferences.screens.languages.LanguagesScreen;
@ -110,6 +111,8 @@ public class PreferencesActivity extends AppCompatActivity implements Preference
return new AppearanceScreen(this);
case DebugScreen.NAME:
return new DebugScreen(this);
case DeleteWordsScreen.NAME:
return new DeleteWordsScreen(this);
case HotkeysScreen.NAME:
return new HotkeysScreen(this);
case KeyPadScreen.NAME:

View file

@ -278,6 +278,7 @@ public class SettingsStore {
}
}
public final static int DELETE_WORDS_SEARCH_DELAY = 500; // ms
public final static int DICTIONARY_AUTO_LOAD_COOLDOWN_TIME = 120000; // ms
public final static int DICTIONARY_IMPORT_BATCH_SIZE = 5000; // words
public final static int DICTIONARY_IMPORT_PROGRESS_UPDATE_TIME = 250; // ms
@ -286,7 +287,6 @@ public class SettingsStore {
public final static float SOFT_KEY_COMPLEX_LABEL_TITLE_SIZE = 0.55f;
public final static float SOFT_KEY_COMPLEX_LABEL_ARABIC_TITLE_SIZE = 0.72f;
public final static float SOFT_KEY_COMPLEX_LABEL_SUB_TITLE_SIZE = 0.8f;
public final static int SUGGESTIONS_MAX = 20;
public final static int SUGGESTIONS_MIN = 8;
public final static int SUGGESTIONS_SELECT_ANIMATION_DURATION = 66;

View file

@ -0,0 +1,58 @@
package io.github.sspanak.tt9.preferences.screens.deleteWords;
import androidx.annotation.NonNull;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import java.util.ArrayList;
import io.github.sspanak.tt9.R;
class DeletableWordsList {
static final String NAME = "delete_words_list";
private final PreferenceCategory item;
DeletableWordsList(Preference preference) {
item = preference instanceof PreferenceCategory ? (PreferenceCategory) preference : null;
}
private void clear() {
if (item != null) {
item.removeAll();
}
}
private void addWord(String word) {
if (item != null) {
PreferenceDeletableWord pref = new PreferenceDeletableWord(item.getContext());
pref.setWord(word);
pref.setLayoutResource(R.layout.pref_deletable_word);
item.addPreference(pref);
}
}
void addWords(ArrayList<String> words) {
for (String word : words) {
addWord(word);
}
}
void addNoResult(boolean noSearchTerm) {
if (item != null) {
Preference pref = new Preference(item.getContext());
pref.setSummary(noSearchTerm ? "--" : item.getContext().getString(R.string.delete_words_no_result));
pref.setLayoutResource(R.layout.pref_text);
item.addPreference(pref);
}
}
void setResult(@NonNull String searchTerm, ArrayList<String> words) {
clear();
if (words == null || words.isEmpty()) {
addNoResult(searchTerm.isEmpty());
} else {
addWords(words);
}
}
}

View file

@ -0,0 +1,30 @@
package io.github.sspanak.tt9.preferences.screens.deleteWords;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
public class DeleteWordsScreen extends BaseScreenFragment {
final public static String NAME = "DeleteWords";
public DeleteWordsScreen() { init(); }
public DeleteWordsScreen(PreferencesActivity activity) { init(activity); }
@Override public String getName() { return NAME; }
@Override protected int getTitle() { return R.string.pref_category_delete_words; }
@Override protected int getXml() { return R.xml.prefs_screen_delete_words; }
@Override protected void onCreate() {
createPage();
}
private void createPage() {
DeletableWordsList searchResultsList = new DeletableWordsList(findPreference(DeletableWordsList.NAME));
searchResultsList.setResult("", null);
PreferenceSearchWords searchWords = findPreference(PreferenceSearchWords.NAME);
if (searchWords != null) {
searchWords.setOnWordsHandler((words) -> searchResultsList.setResult(searchWords.getLastSearchTerm(), words));
}
}
}

View file

@ -0,0 +1,72 @@
package io.github.sspanak.tt9.preferences.screens.deleteWords;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceViewHolder;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.WordStoreAsync;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.SettingsStore;
import io.github.sspanak.tt9.ui.UI;
public class PreferenceDeletableWord extends Preference {
private String word;
public PreferenceDeletableWord(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
public PreferenceDeletableWord(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
public PreferenceDeletableWord(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); }
public PreferenceDeletableWord(@NonNull Context context) { super(context); }
public void setWord(String word) {
this.word = word;
setTitle(word);
}
@Override
protected void onClick() {
super.onClick();
Context context = getContext();
UI.confirm(
context,
context.getString(R.string.delete_words_deleted_confirm_deletion_title),
context.getString(R.string.delete_words_deleted_confirm_deletion_question, word),
context.getString(R.string.delete_words_deleted_confirm_deletion_yes),
this::onDeletionConfirmed,
null
);
}
private void onDeletionConfirmed() {
SettingsStore settings = new SettingsStore(getContext());
WordStoreAsync.deleteCustomWord(
this::onWordDeleted,
LanguageCollection.getLanguage(getContext(), settings.getInputLanguage()),
word
);
}
private void onWordDeleted() {
if (getParent() instanceof PreferenceCategory) {
getParent().removePreference(this);
}
Activity activity = (Activity) getContext();
activity.runOnUiThread(
() -> UI.toastFromAsync(getContext(), activity.getString(R.string.delete_words_deleted_x, word))
);
}
}

View file

@ -0,0 +1,81 @@
package io.github.sspanak.tt9.preferences.screens.deleteWords;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import java.util.ArrayList;
import io.github.sspanak.tt9.ConsumerCompat;
import io.github.sspanak.tt9.Logger;
import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.WordStoreAsync;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.SettingsStore;
public class PreferenceSearchWords extends Preference {
public static final String NAME = "dictionary_delete_words_search";
private static final String LOG_TAG = PreferenceSearchWords.class.getSimpleName();
private ConsumerCompat<ArrayList<String>> onWords;
private SettingsStore settings;
@NonNull private String lastSearchTerm = "";
public PreferenceSearchWords(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
public PreferenceSearchWords(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
public PreferenceSearchWords(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); }
public PreferenceSearchWords(@NonNull Context context) { super(context); }
@Override
public void onBindViewHolder(@NonNull PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
EditText editText = holder.itemView.findViewById(R.id.input_text_input_field);
if (editText == null) {
Logger.e(LOG_TAG, "Cannot attach a text change listener. Unable to find the EditText element.");
} else {
editText.addTextChangedListener(TextChangeListener.getInstance(this::onChange));
}
}
@NonNull
public String getLastSearchTerm() {
return lastSearchTerm;
}
private SettingsStore getSettings() {
if (settings == null) {
settings = new SettingsStore(getContext());
}
return settings;
}
private void onChange(String word) {
lastSearchTerm = word == null || word.trim().isEmpty() ? "" : word.trim();
if (onWords == null) {
Logger.w(LOG_TAG, "No handler set for the word change event.");
} else if (lastSearchTerm.isEmpty()) {
Logger.d(LOG_TAG, "Not searching for an empty word.");
onWords.accept(null);
} else {
Language currentLanguage = LanguageCollection.getLanguage(getContext(), getSettings().getInputLanguage());
WordStoreAsync.getCustomWords(onWords, currentLanguage, lastSearchTerm);
}
}
void setOnWordsHandler(ConsumerCompat<ArrayList<String>> onWords) {
this.onWords = onWords;
}
}

View file

@ -0,0 +1,39 @@
package io.github.sspanak.tt9.preferences.screens.deleteWords;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
import androidx.annotation.NonNull;
import io.github.sspanak.tt9.ConsumerCompat;
import io.github.sspanak.tt9.preferences.SettingsStore;
class TextChangeListener implements TextWatcher {
private static TextChangeListener self;
@NonNull private ConsumerCompat<String> onChange;
@NonNull private final Handler debouncer = new Handler(Looper.getMainLooper());
private TextChangeListener(@NonNull ConsumerCompat<String> onChange) {
this.onChange = onChange;
}
static TextChangeListener getInstance(@NonNull ConsumerCompat<String> onChange) {
if (self == null) {
self = new TextChangeListener(onChange);
}
self.onChange = onChange;
return self;
}
@Override public void afterTextChanged(Editable s) {
debouncer.removeCallbacksAndMessages(null);
debouncer.postDelayed(() -> onChange.accept(s.toString()), SettingsStore.DELETE_WORDS_SEARCH_DELAY);
}
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {}
}

View file

@ -0,0 +1,11 @@
package io.github.sspanak.tt9.preferences.screens.languages;
import androidx.preference.Preference;
import io.github.sspanak.tt9.preferences.items.ItemClickable;
class ItemDeleteCustomWords extends ItemClickable {
final static String NAME = "screen_delete_words";
ItemDeleteCustomWords(Preference item) { super(item); }
@Override protected boolean onClick(Preference p) { return false; }
}

View file

@ -39,8 +39,6 @@ class ItemLoadDictionary extends ItemClickable {
this.onStart = onStart;
this.onFinish = onFinish;
loader.setOnStatusChange(this::onLoadingStatusChange);
refreshStatus();
}
@ -85,6 +83,7 @@ class ItemLoadDictionary extends ItemClickable {
private void setLoadingStatus() {
loader.setOnStatusChange(this::onLoadingStatusChange);
onStart.run();
item.setTitle(context.getString(R.string.dictionary_cancel_load));
}

View file

@ -67,6 +67,8 @@ public class LanguagesScreen extends BaseScreenFragment {
this::onActionFinish
));
clickables.add(new ItemDeleteCustomWords(findPreference(ItemDeleteCustomWords.NAME)));
exportCustomWordsItem = new ItemExportCustomWords(
findPreference(ItemExportCustomWords.NAME),
activity,

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/pref_padding_horizontal"
android:paddingVertical="@dimen/pref_padding_vertical"
app:layout_anchorGravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingEnd="15dp"
android:text="✕"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.TextView"
android:textSize="@dimen/soft_key_icon_size"
tools:ignore="HardcodedText" />
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.DeviceDefault.Widget.TextView"
android:textSize="@dimen/pref_text_size"
tools:text="Lorem" />
</LinearLayout>

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:descendantFocusability="afterDescendants"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/pref_padding_horizontal"
android:paddingVertical="@dimen/pref_padding_vertical">
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/input_text_input_field"
android:text="?android:title" />
<EditText
android:id="@+id/input_text_input_field"
android:importantForAutofill="no"
android:inputType="text"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textSize="@dimen/pref_text_size" />
<TextView android:id="@android:id/summary"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
android:textSize="@dimen/pref_summary_size" />
</LinearLayout>

View file

@ -35,7 +35,7 @@
<string name="key_back">Назад</string>
<string name="key_call">Зелена слушалка</string>
<string name="dictionary_export">Експортирай избраните</string>
<string name="dictionary_export_custom_words_button">Експортирай</string>
<string name="dictionary_export_custom_words">Експортирай</string>
<string name="dictionary_export_custom_words_summary">Експортиране на CSV с всички добавени думи в: „%1$s“.</string>
<string name="dictionary_export_failed">Неуспешно експортиране</string>
<string name="dictionary_export_failed_more_info">За повече информация, активирайте режима за отстраняване на грешки и прегледайте журнала.</string>

View file

@ -32,7 +32,7 @@
<string name="pref_dark_theme_no">Nein</string>
<string name="pref_dark_theme_auto">Automatisch</string>
<string name="dictionary_export">Ausgewählte exportieren</string>
<string name="dictionary_export_custom_words_button">Exportieren</string>
<string name="dictionary_export_custom_words">Exportieren</string>
<string name="dictionary_export_custom_words_summary">Exportiere ein CSV mit allen hinzugefügten Wörtern nach: „%1$s“.</string>
<string name="dictionary_export_failed">Export fehlgeschlagen</string>
<string name="dictionary_export_failed_more_info">Für weitere Informationen, aktivieren Sie den Debug-Modus und sehen Sie sich die Protokolle an.</string>

View file

@ -75,7 +75,7 @@
<string name="pref_dark_theme_no">No</string>
<string name="pref_dark_theme_auto">Automática</string>
<string name="dictionary_export">Exportar seleccionados</string>
<string name="dictionary_export_custom_words_button">Exportar</string>
<string name="dictionary_export_custom_words">Exportar</string>
<string name="dictionary_export_custom_words_summary">Exportar un CSV con todas las palabras añadidas a: \"%1$s\".</string>
<string name="dictionary_export_failed">Fallo en la exportación</string>
<string name="dictionary_export_failed_more_info">Para obtener más información, habilita el modo de depuración y consulta los registros.</string>

View file

@ -75,7 +75,7 @@
<string name="pref_dark_theme_auto">Automatique</string>
<string name="add_word_confirm">Ajouter mot « %1$s » à %2$s?</string>
<string name="dictionary_export">Exporter les sélectionées</string>
<string name="dictionary_export_custom_words_button">Exporter</string>
<string name="dictionary_export_custom_words">Exporter</string>
<string name="dictionary_export_custom_words_summary">Exporter un CSV avec tous les mots ajoutés vers : «%1$s».</string>
<string name="dictionary_export_failed">Échec de l\'exportation</string>
<string name="dictionary_export_failed_more_info">Pour plus d\'informations, activez le mode de débogage et consultez les journaux.</string>

View file

@ -41,7 +41,7 @@
<string name="pref_dark_theme_no">No</string>
<string name="pref_dark_theme_auto">Automatica</string>
<string name="dictionary_export">Esporta selezionate</string>
<string name="dictionary_export_custom_words_button">Esportare</string>
<string name="dictionary_export_custom_words">Esportare</string>
<string name="dictionary_export_custom_words_summary">Esporta un CSV con tutte le parole aggiunte su: \"%1$s\".</string>
<string name="dictionary_export_failed">Esportazione fallita</string>
<string name="dictionary_export_failed_more_info">Per ulteriori informazioni, abilita la modalità di debug e consulta i log.</string>

View file

@ -68,7 +68,7 @@
<string name="pref_dark_theme_no">לא</string>
<string name="pref_dark_theme_auto">אוטומטי</string>
<string name="dictionary_export">ייצוא שנבחר</string>
<string name="dictionary_export_custom_words_button">לְיְצוֹא</string>
<string name="dictionary_export_custom_words">לְיְצוֹא</string>
<string name="dictionary_export_custom_words_summary">ייצוא CSV עם כל המילים שנוספו ל: \"%1$s\".</string>
<string name="dictionary_export_failed">נכשל בייצוא</string>
<string name="dictionary_export_failed_more_info">"למידע נוסף, הפעל מצב איתור באגים וראה את הלוגים. "</string>

View file

@ -32,7 +32,7 @@
<string name="pref_dark_theme_no">Nee</string>
<string name="pref_dark_theme_auto">Automatisch</string>
<string name="dictionary_export">Geselecteerde exporteren</string>
<string name="dictionary_export_custom_words_button">Exporteren</string>
<string name="dictionary_export_custom_words">Exporteren</string>
<string name="dictionary_export_custom_words_summary">Exporteer een CSV met alle toegevoegde woorden naar: \"%1$s\".</string>
<string name="dictionary_export_failed">Exporteren mislukt</string>
<string name="dictionary_export_failed_more_info">Voor meer informatie, schakel de debug-modus in en bekijk de logs.</string>

View file

@ -64,7 +64,7 @@
<string name="pref_dark_theme_no">Não</string>
<string name="pref_dark_theme_auto">Automático</string>
<string name="dictionary_export">Exportar selecionados</string>
<string name="dictionary_export_custom_words_button">Exportar</string>
<string name="dictionary_export_custom_words">Exportar</string>
<string name="dictionary_export_custom_words_summary">Exportar um CSV com todas as palavras adicionadas para: \"%1$s\".</string>
<string name="dictionary_export_failed">Falha na exportação</string>
<string name="dictionary_export_failed_more_info">Para mais informações, ative o modo de depuração e veja os registros.</string>

View file

@ -80,7 +80,7 @@
<string name="pref_dark_theme_auto">Автоматически</string>
<string name="add_word_confirm">Добавить слово «%1$s» в %2$s?</string>
<string name="dictionary_export">Экспортировать выбранные</string>
<string name="dictionary_export_custom_words_button">Экспортировать</string>
<string name="dictionary_export_custom_words">Экспортировать</string>
<string name="dictionary_export_custom_words_summary">Экспорт CSV со всеми добавленными словами в: «%1$s».</string>
<string name="dictionary_export_failed">Ошибка экспорта</string>
<string name="dictionary_export_failed_more_info">Для получения дополнительной информации включите режим отладки и просмотрите журналы.</string>

View file

@ -65,7 +65,7 @@
<string name="dictionary_truncating">Видаляється…</string>
<string name="dictionary_export">Експортувати вибрані</string>
<string name="dictionary_export_custom_words_button">Експортувати</string>
<string name="dictionary_export_custom_words">Експортувати</string>
<string name="dictionary_export_custom_words_summary">Експорт CSV з усіма доданими словами в: \"%1$s\".</string>
<string name="dictionary_export_failed">Помилка експорту</string>
<string name="dictionary_export_failed_more_info">Для отримання додаткової інформації увімкніть режим відлагодження та перегляньте журнали.</string>

View file

@ -23,6 +23,7 @@
<string name="pref_category_about">About</string>
<string name="pref_category_abc_mode">ABC Mode</string>
<string name="pref_category_custom_words">Added Words</string>
<string name="pref_category_delete_words">Delete Added Words</string>
<string name="pref_category_hacks">Compatibility</string>
<string name="pref_category_appearance">Appearance</string>
<string name="pref_category_debug_options" translatable="false">Debug Options</string>
@ -73,7 +74,7 @@
<string name="dictionary_truncating">Deleting…</string>
<string name="dictionary_export">Export Selected</string>
<string name="dictionary_export_custom_words_button">Export</string>
<string name="dictionary_export_custom_words">Export</string>
<string name="dictionary_export_custom_words_summary">Export a CSV with all added words in: \"%1$s\".</string>
<string name="dictionary_export_failed">Exporting Failed</string>
<string name="dictionary_export_failed_more_info">For more info, enable debugging mode and see the logs.</string>
@ -82,6 +83,16 @@
<string name="dictionary_export_generating_csv">Exporting CSV…</string>
<string name="dictionary_export_generating_csv_for_language">Exporting CSV (%1$s)…</string>
<string name="delete_words_link">Delete</string>
<string name="delete_words_link_summary">Search and delete misspelled or unneeded words.</string>
<string name="delete_words_search_placeholder">Search Words</string>
<string name="delete_words_list">Search Results</string>
<string name="delete_words_no_result">No results</string>
<string name="delete_words_deleted_confirm_deletion_title">Confirm Deletion</string>
<string name="delete_words_deleted_confirm_deletion_question">Are you sure you want to delete \"%1$s\"?</string>
<string name="delete_words_deleted_confirm_deletion_yes">Delete</string>
<string name="delete_words_deleted_x">\"%1$s\" was deleted.</string>
<string name="dictionary_update_message">Dictionary update available for \"%1$s\". Would you like to load it?</string>
<string name="dictionary_update_update">Load</string>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:orderingFromXml="true">
<io.github.sspanak.tt9.preferences.screens.deleteWords.PreferenceSearchWords
android:layout="@layout/pref_input_text"
android:key="dictionary_delete_words_search"
android:title="@string/delete_words_search_placeholder" />
<PreferenceCategory
android:key="delete_words_list"
android:title="@string/delete_words_list"
android:layout="@layout/pref_category" />
</PreferenceScreen>

View file

@ -35,7 +35,14 @@
<Preference
app:key="dictionary_export_custom"
app:layout="@layout/pref_text"
app:title="@string/dictionary_export_custom_words_button" />
app:title="@string/dictionary_export_custom_words" />
<Preference
app:fragment="io.github.sspanak.tt9.preferences.DeleteWordsScreen"
app:key="screen_delete_words"
app:layout="@layout/pref_text"
app:title="@string/delete_words_link"
app:summary="@string/delete_words_link_summary"/>
</PreferenceCategory>