1
0
Fork 0

fixed the delete dictionary buttons not staying locked when navigating from and to the Languages screen, while deletion is in progress

This commit is contained in:
sspanak 2025-01-06 17:32:19 +02:00 committed by Dimo Karaivanov
parent fec7d62168
commit f7e11c7445
7 changed files with 152 additions and 59 deletions

View file

@ -11,7 +11,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import io.github.sspanak.tt9.db.entities.AddWordResult; import io.github.sspanak.tt9.db.entities.AddWordResult;
import io.github.sspanak.tt9.db.wordPairs.WordPairStore; import io.github.sspanak.tt9.db.wordPairs.WordPairStore;
@ -21,7 +20,6 @@ import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.preferences.settings.SettingsStore; import io.github.sspanak.tt9.preferences.settings.SettingsStore;
import io.github.sspanak.tt9.util.ConsumerCompat; import io.github.sspanak.tt9.util.ConsumerCompat;
import io.github.sspanak.tt9.util.Logger; import io.github.sspanak.tt9.util.Logger;
import io.github.sspanak.tt9.util.Timer;
public class DataStore { public class DataStore {
private final static String LOG_TAG = DataStore.class.getSimpleName(); private final static String LOG_TAG = DataStore.class.getSimpleName();
@ -80,27 +78,6 @@ public class DataStore {
} }
public static void deleteLanguages(Runnable notification, @NonNull ArrayList<Language> languages) {
Timer.start(LOG_TAG);
AtomicInteger progress = new AtomicInteger(languages.size());
Runnable onDeleted = () -> {
if (progress.decrementAndGet() == 0) {
Logger.d(LOG_TAG, "Deleted " + languages.size() + " languages. Time: " + Timer.stop(LOG_TAG) + " ms");
notification.run();
}
};
for (Language language : languages) {
runInTransaction(
() -> { words.remove(language); pairs.remove(language); },
onDeleted,
"Failed deleting languages."
);
}
}
public static void put(ConsumerCompat<AddWordResult> statusHandler, Language language, String word) { public static void put(ConsumerCompat<AddWordResult> statusHandler, Language language, String word) {
runInThread(() -> statusHandler.accept(words.put(language, word))); runInThread(() -> statusHandler.accept(words.put(language, word)));
} }

View file

@ -165,17 +165,6 @@ public class WordPairStore extends BaseSyncStore {
} }
public void remove(@NonNull Language language) {
if (!checkOrNotify()) {
return;
}
DeleteOps.deleteWordPairs(sqlite.getDb(), language.getId());
slowestLoadTime = 0;
slowestSaveTime = 0;
}
public void remove(@NonNull ArrayList<Language> languages) { public void remove(@NonNull ArrayList<Language> languages) {
if (!checkOrNotify()) { if (!checkOrNotify()) {
return; return;

View file

@ -0,0 +1,106 @@
package io.github.sspanak.tt9.db.words;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import io.github.sspanak.tt9.db.BaseSyncStore;
import io.github.sspanak.tt9.db.sqlite.DeleteOps;
import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.NaturalLanguage;
import io.github.sspanak.tt9.util.Logger;
import io.github.sspanak.tt9.util.Timer;
public class DictionaryDeleter extends BaseSyncStore {
private static final String LOG_TAG = DictionaryDeleter.class.getSimpleName();
private static DictionaryDeleter self;
@NonNull private final ExecutorService executor = Executors.newSingleThreadExecutor();
@Nullable Future<?> deleteTask;
@Nullable private Runnable notification;
protected DictionaryDeleter(Context context) {
super(context);
}
public static DictionaryDeleter getInstance(Context context) {
if (self == null) {
self = new DictionaryDeleter(context);
}
return self;
}
public void deleteLanguages(@NonNull ArrayList<Language> languages) {
if (!checkOrNotify()) {
onFinish();
return;
}
Timer.start(LOG_TAG);
deleteTask = executor.submit(() -> deleteLanguagesSync(languages));
}
private void deleteLanguagesSync(@NonNull ArrayList<Language> languages) {
for (Language language : languages) {
if (!deleteLanguage(language)) {
break;
}
}
deleteTask = null;
if (notification != null) {
notification.run();
}
Logger.d(LOG_TAG, "Deleted " + languages.size() + " languages. Time: " + Timer.stop(LOG_TAG) + " ms");
}
private boolean deleteLanguage(Language language) {
if (!(language instanceof NaturalLanguage)) {
Logger.w(LOG_TAG, "Invalid language type to delete: " + language.getClass().getSimpleName() + ". Skipping.");
return true;
}
try {
sqlite.beginTransaction();
DeleteOps.delete(sqlite.getDb(), language.getId());
DeleteOps.deleteWordPairs(sqlite.getDb(), language.getId());
sqlite.finishTransaction();
} catch (Exception e) {
sqlite.failTransaction();
Logger.e(LOG_TAG, "Failed deleting language: " + language.getId() + ". " + e.getMessage());
return false;
}
return true;
}
private void onFinish() {
if (notification != null) {
notification.run();
}
}
public boolean isRunning() {
return deleteTask != null && !deleteTask.isDone();
}
public void setOnFinish(Runnable notification) {
this.notification = notification;
}
}

View file

@ -118,13 +118,6 @@ public class WordStore extends BaseSyncStore {
} }
public void remove(Language language) {
if (checkOrNotify() && readOps.exists(sqlite.getDb(), language.getId())) {
DeleteOps.delete(sqlite.getDb(), language.getId());
}
}
public void removeCustomWord(Language language, String word) { public void removeCustomWord(Language language, String word) {
if (language == null || language instanceof NullLanguage || !checkOrNotify()) { if (language == null || language instanceof NullLanguage || !checkOrNotify()) {
return; return;

View file

@ -3,7 +3,7 @@ package io.github.sspanak.tt9.preferences.screens.languages;
import androidx.preference.Preference; import androidx.preference.Preference;
import io.github.sspanak.tt9.R; import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.DataStore; import io.github.sspanak.tt9.db.words.DictionaryDeleter;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.preferences.items.ItemClickable; import io.github.sspanak.tt9.preferences.items.ItemClickable;
@ -14,6 +14,7 @@ class ItemTruncateAll extends ItemClickable {
public static final String NAME = "dictionary_truncate"; public static final String NAME = "dictionary_truncate";
protected final PreferencesActivity activity; protected final PreferencesActivity activity;
protected final DictionaryDeleter deleter;
private final Runnable onStart; private final Runnable onStart;
private final Runnable onFinish; private final Runnable onFinish;
@ -21,31 +22,49 @@ class ItemTruncateAll extends ItemClickable {
ItemTruncateAll(Preference item, PreferencesActivity activity, Runnable onStart, Runnable onFinish) { ItemTruncateAll(Preference item, PreferencesActivity activity, Runnable onStart, Runnable onFinish) {
super(item); super(item);
this.activity = activity; this.activity = activity;
this.deleter = DictionaryDeleter.getInstance(activity);
this.onStart = onStart; this.onStart = onStart;
this.onFinish = onFinish; this.onFinish = onFinish;
refreshStatus();
} }
@Override @Override
protected boolean onClick(Preference p) { protected boolean onClick(Preference p) {
onStartDeleting(); onStart.run();
DataStore.deleteLanguages(this::onFinishDeleting, LanguageCollection.getAll(false)); setBusy();
deleter.deleteLanguages(LanguageCollection.getAll(false));
return true; return true;
} }
protected void onStartDeleting() { void refreshStatus() {
onStart.run(); if (deleter.isRunning()) {
disable(); setBusy();
} else {
enable();
}
}
protected void setBusy() {
deleter.setOnFinish(this::onFinishDeleting);
item.setSummary(R.string.dictionary_truncating); item.setSummary(R.string.dictionary_truncating);
disable();
}
public void enable() {
super.enable();
item.setSummary("");
} }
protected void onFinishDeleting() { protected void onFinishDeleting() {
activity.runOnUiThread(() -> { activity.runOnUiThread(() -> {
onFinish.run(); onFinish.run();
item.setSummary("");
enable(); enable();
UI.toastFromAsync(activity, R.string.dictionary_truncated); UI.toastFromAsync(activity, R.string.dictionary_truncated);
}); });

View file

@ -6,7 +6,6 @@ import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import io.github.sspanak.tt9.db.DataStore;
import io.github.sspanak.tt9.languages.Language; import io.github.sspanak.tt9.languages.Language;
import io.github.sspanak.tt9.languages.LanguageCollection; import io.github.sspanak.tt9.languages.LanguageCollection;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
@ -31,8 +30,9 @@ class ItemTruncateUnselected extends ItemTruncateAll {
} }
} }
onStartDeleting(); setBusy();
DataStore.deleteLanguages(this::onFinishDeleting, unselectedLanguages); deleter.setOnFinish(this::onFinishDeleting);
deleter.deleteLanguages(unselectedLanguages);
return true; return true;
} }

View file

@ -11,6 +11,7 @@ import io.github.sspanak.tt9.R;
import io.github.sspanak.tt9.db.customWords.CustomWordsExporter; import io.github.sspanak.tt9.db.customWords.CustomWordsExporter;
import io.github.sspanak.tt9.db.customWords.CustomWordsImporter; import io.github.sspanak.tt9.db.customWords.CustomWordsImporter;
import io.github.sspanak.tt9.db.customWords.DictionaryExporter; import io.github.sspanak.tt9.db.customWords.DictionaryExporter;
import io.github.sspanak.tt9.db.words.DictionaryDeleter;
import io.github.sspanak.tt9.db.words.DictionaryLoader; import io.github.sspanak.tt9.db.words.DictionaryLoader;
import io.github.sspanak.tt9.preferences.PreferencesActivity; import io.github.sspanak.tt9.preferences.PreferencesActivity;
import io.github.sspanak.tt9.preferences.items.ItemClickable; import io.github.sspanak.tt9.preferences.items.ItemClickable;
@ -19,12 +20,14 @@ import io.github.sspanak.tt9.preferences.screens.BaseScreenFragment;
public class LanguagesScreen extends BaseScreenFragment { public class LanguagesScreen extends BaseScreenFragment {
public static final String NAME = "Languages"; public static final String NAME = "Languages";
private final ArrayList<ItemClickable> clickables = new ArrayList<>(); private static final ArrayList<ItemClickable> clickables = new ArrayList<>();
private ItemLoadDictionary loadItem; private ItemLoadDictionary loadItem;
private ItemImportCustomWords importCustomWordsItem; private ItemImportCustomWords importCustomWordsItem;
private ItemExportDictionary exportDictionaryItem; private ItemExportDictionary exportDictionaryItem;
private ItemExportCustomWords exportCustomWordsItem; private ItemExportCustomWords exportCustomWordsItem;
private ItemTruncateAll truncateAllItem;
private ItemTruncateUnselected truncateUnselectedItem;
public LanguagesScreen() { init(); } public LanguagesScreen() { init(); }
public LanguagesScreen(PreferencesActivity activity) { init(activity); } public LanguagesScreen(PreferencesActivity activity) { init(activity); }
@ -56,22 +59,25 @@ public class LanguagesScreen extends BaseScreenFragment {
this::onActionFinish this::onActionFinish
); );
clickables.add(loadItem); truncateUnselectedItem = new ItemTruncateUnselected(
clickables.add(exportDictionaryItem);
clickables.add(new ItemTruncateUnselected(
findPreference(ItemTruncateUnselected.NAME), findPreference(ItemTruncateUnselected.NAME),
activity, activity,
this::onActionStart, this::onActionStart,
this::onActionFinish this::onActionFinish
)); );
clickables.add(new ItemTruncateAll( truncateAllItem = new ItemTruncateAll(
findPreference(ItemTruncateAll.NAME), findPreference(ItemTruncateAll.NAME),
activity, activity,
this::onActionStart, this::onActionStart,
this::onActionFinish this::onActionFinish
)); );
clickables.clear();
clickables.add(loadItem);
clickables.add(exportDictionaryItem);
clickables.add(truncateUnselectedItem);
clickables.add(truncateAllItem);
clickables.add(new ItemDeleteCustomWords(findPreference(ItemDeleteCustomWords.NAME))); clickables.add(new ItemDeleteCustomWords(findPreference(ItemDeleteCustomWords.NAME)));
@ -112,6 +118,8 @@ public class LanguagesScreen extends BaseScreenFragment {
exportDictionaryItem.refreshStatus(); exportDictionaryItem.refreshStatus();
exportCustomWordsItem.refreshStatus(); exportCustomWordsItem.refreshStatus();
importCustomWordsItem.refreshStatus(); importCustomWordsItem.refreshStatus();
truncateUnselectedItem.refreshStatus();
truncateAllItem.refreshStatus();
if (DictionaryLoader.getInstance(activity).isRunning()) { if (DictionaryLoader.getInstance(activity).isRunning()) {
loadItem.refreshStatus(); loadItem.refreshStatus();
@ -120,6 +128,7 @@ public class LanguagesScreen extends BaseScreenFragment {
CustomWordsExporter.getInstance().isRunning() CustomWordsExporter.getInstance().isRunning()
|| DictionaryExporter.getInstance().isRunning() || DictionaryExporter.getInstance().isRunning()
|| CustomWordsImporter.getInstance(activity).isRunning() || CustomWordsImporter.getInstance(activity).isRunning()
|| DictionaryDeleter.getInstance(activity).isRunning()
) { ) {
onActionStart(); onActionStart();
} else { } else {