* dictionary exporting
* removed unnecessary passing of DictionaryLoader and DictionaryLoadingBar between the preference fragments
This commit is contained in:
parent
f4c425516e
commit
29b2ac2cb6
30 changed files with 953 additions and 203 deletions
|
|
@ -43,6 +43,7 @@ So make sure to read the initial setup and the hotkey tips in the [user manual](
|
|||
## ⌨ Contributing
|
||||
As with many other open-source projects, this one is also maintained by its author in his free time. Any help in making Traditional T9 better will be highly appreciated. Here is how:
|
||||
- Add [a new language](CONTRIBUTING.md#adding-a-new-language), [new UI translations](CONTRIBUTING.md#translating-the-ui) or simply fix a spelling mistake. The process is very simple and even with minimum technical knowledge, your skills as a native speaker will be of great use. Or, if you are not tech-savvy, just [open a new issue](https://github.com/sspanak/tt9/issues) and put the correct translations or words there. Correcting misspelled words or adding new ones is the best you can do to help. Processing millions of words in multiple languages is a very difficult task for a single person.
|
||||
- Share your list of added words. Use the Export function in: Settings → Languages → Added Words and upload the generated CSV file in a [new issue](https://github.com/sspanak/tt9/issues). You are also welcome to [open a PR](https://github.com/sspanak/tt9/pulls), if you have good technical knowledge and can split them by language.
|
||||
- [Report bugs](https://github.com/sspanak/tt9/issues) or other unusual behavior on different phones. Currently, the only testing and development devices are: Qin F21 Pro+ / Android 11; Energizer H620SEU / Android 10; Vodaphone VFD 500 / Android 6.0. But Android behaviour and appearance varies a lot across the millions of devices available out there.
|
||||
- Experienced developers who are willing fix a bug, or maybe create a brand new feature, see the [Contribution Guide](CONTRIBUTING.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
android:versionCode="383"
|
||||
android:versionName="28.0"
|
||||
android:versionCode="395"
|
||||
android:versionName="28.12"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||
<!-- required for words exporting on Android < 10 -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="false"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
package io.github.sspanak.tt9.db.exporter;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import io.github.sspanak.tt9.ConsumerCompat;
|
||||
|
||||
public abstract class AbstractExporter {
|
||||
final protected static String FILE_EXTENSION = ".csv";
|
||||
final protected static String MIME_TYPE = "text/csv";
|
||||
|
||||
protected Runnable failureHandler;
|
||||
protected ConsumerCompat<String> successHandler;
|
||||
protected Thread processThread;
|
||||
private String outputFile;
|
||||
|
||||
|
||||
public static AbstractExporter getInstance() {
|
||||
throw new RuntimeException("Not Implemented");
|
||||
}
|
||||
|
||||
|
||||
private void writeAndroid10(Activity activity) throws Exception {
|
||||
final String fileName = generateFileName();
|
||||
outputFile = getOutputDir() + File.pathSeparator + fileName;
|
||||
|
||||
final ContentValues file = new ContentValues();
|
||||
file.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);
|
||||
file.put(MediaStore.MediaColumns.MIME_TYPE, MIME_TYPE);
|
||||
file.put(MediaStore.MediaColumns.RELATIVE_PATH, getOutputDir());
|
||||
|
||||
final ContentResolver resolver = activity.getContentResolver();
|
||||
Uri uri = null;
|
||||
|
||||
try {
|
||||
uri = resolver.insert(MediaStore.Files.getContentUri("external"), file);
|
||||
if (uri == null) {
|
||||
throw new IOException("Failed to create new MediaStore entry.");
|
||||
}
|
||||
|
||||
try (OutputStream stream = resolver.openOutputStream(uri)) {
|
||||
if (stream == null) {
|
||||
throw new IOException("Failed to open output stream.");
|
||||
}
|
||||
stream.write(getWords(activity));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (uri != null) {
|
||||
resolver.delete(uri, null, null);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void writeLegacy(Activity activity) throws Exception {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
|
||||
}
|
||||
|
||||
final String exportDir = Environment.getExternalStoragePublicDirectory(getOutputDir()).getAbsolutePath();
|
||||
final String fileName = generateFileName();
|
||||
outputFile = getOutputDir() + File.pathSeparator + fileName;
|
||||
|
||||
final File file = new File(exportDir, fileName);
|
||||
if (!file.createNewFile()) {
|
||||
throw new IOException("Failed to create a new file.");
|
||||
}
|
||||
|
||||
try (OutputStream stream = new FileOutputStream(file)) {
|
||||
stream.write(getWords(activity));
|
||||
}
|
||||
|
||||
MediaScannerConnection.scanFile(activity, new String[]{file.getAbsolutePath()}, new String[]{MIME_TYPE}, null);
|
||||
}
|
||||
|
||||
|
||||
protected void write(Activity activity) throws Exception {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
writeAndroid10(activity);
|
||||
} else {
|
||||
writeLegacy(activity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected String getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
|
||||
public String getOutputDir() {
|
||||
// on some older phones, files may not be visible in the DOCUMENTS directory, so we use DOWNLOADS
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? Environment.DIRECTORY_DOCUMENTS : Environment.DIRECTORY_DOWNLOADS;
|
||||
}
|
||||
|
||||
|
||||
protected void sendSuccess() {
|
||||
if (successHandler != null) {
|
||||
successHandler.accept(outputFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void sendFailure() {
|
||||
if (failureHandler != null) {
|
||||
failureHandler.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isRunning() {
|
||||
return processThread != null && processThread.isAlive();
|
||||
}
|
||||
|
||||
|
||||
public void setSuccessHandler(ConsumerCompat<String> handler) {
|
||||
successHandler = handler;
|
||||
}
|
||||
|
||||
|
||||
public void setFailureHandler(Runnable handler) {
|
||||
failureHandler = handler;
|
||||
}
|
||||
|
||||
|
||||
@NonNull abstract protected String generateFileName();
|
||||
@NonNull abstract protected byte[] getWords(Activity activity) throws Exception;
|
||||
abstract public boolean export(Activity activity);
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package io.github.sspanak.tt9.db.exporter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import io.github.sspanak.tt9.db.sqlite.ReadOps;
|
||||
import io.github.sspanak.tt9.db.sqlite.SQLiteOpener;
|
||||
|
||||
public class CustomWordsExporter extends AbstractExporter {
|
||||
private static CustomWordsExporter customWordsExporterSelf;
|
||||
|
||||
public static final String LOG_TAG = "dictionary_export";
|
||||
private static final String BASE_FILE_NAME = "tt9-added-words-export-";
|
||||
|
||||
public static CustomWordsExporter getInstance() {
|
||||
if (customWordsExporterSelf == null) {
|
||||
customWordsExporterSelf = new CustomWordsExporter();
|
||||
}
|
||||
|
||||
return customWordsExporterSelf;
|
||||
}
|
||||
|
||||
public boolean export(Activity activity) {
|
||||
if (isRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
processThread = new Thread(() -> {
|
||||
try {
|
||||
write(activity);
|
||||
sendSuccess();
|
||||
} catch (Exception e) {
|
||||
sendFailure();
|
||||
}
|
||||
});
|
||||
|
||||
processThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected String generateFileName() {
|
||||
return BASE_FILE_NAME + "-" + System.currentTimeMillis() + FILE_EXTENSION;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected byte[] getWords(Activity activity) throws Exception {
|
||||
SQLiteDatabase db = SQLiteOpener.getInstance(activity).getDb();
|
||||
if (db == null) {
|
||||
throw new Exception("Could not open database");
|
||||
}
|
||||
|
||||
return new ReadOps().getWords(db, null, true).getBytes();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
package io.github.sspanak.tt9.db.exporter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.db.sqlite.ReadOps;
|
||||
import io.github.sspanak.tt9.db.sqlite.SQLiteOpener;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
|
||||
public class DictionaryExporter extends AbstractExporter {
|
||||
private static DictionaryExporter self;
|
||||
|
||||
public static final String LOG_TAG = "dictionary_export";
|
||||
private static final String BASE_FILE_NAME = "tt9-dictionary-export-";
|
||||
private ArrayList<Language> languages;
|
||||
private Language currentLanguage;
|
||||
|
||||
public static DictionaryExporter getInstance() {
|
||||
if (self == null) {
|
||||
self = new DictionaryExporter();
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
public boolean export(Activity activity) {
|
||||
if (isRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (languages == null || languages.isEmpty()) {
|
||||
Logger.d(LOG_TAG, "Nothing to do");
|
||||
return true;
|
||||
}
|
||||
|
||||
processThread = new Thread(() -> { for (Language l : languages) exportLanguage(activity, l); });
|
||||
processThread.start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public DictionaryExporter setLanguages(ArrayList<Language> languages) {
|
||||
this.languages = languages;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected String generateFileName() {
|
||||
return BASE_FILE_NAME + currentLanguage.getLocale().getLanguage() + "-" + System.currentTimeMillis() + FILE_EXTENSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
protected byte[] getWords(Activity activity) throws Exception {
|
||||
SQLiteDatabase db = SQLiteOpener.getInstance(activity).getDb();
|
||||
if (db == null) {
|
||||
throw new Exception("Could not open database");
|
||||
}
|
||||
|
||||
return new ReadOps().getWords(db, currentLanguage, false).getBytes();
|
||||
}
|
||||
|
||||
private void exportLanguage(Activity activity, Language language) {
|
||||
currentLanguage = language;
|
||||
if (currentLanguage == null) {
|
||||
Logger.e(LOG_TAG, "Cannot export dictionary for null language");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
write(activity);
|
||||
sendSuccess();
|
||||
Logger.d(LOG_TAG, "All words for language: " + currentLanguage.getName() + " loaded. Time: " + (System.currentTimeMillis() - start) + "ms");
|
||||
} catch (Exception e) {
|
||||
sendFailure();
|
||||
Logger.e(LOG_TAG, "Failed exporting dictionary for " + currentLanguage.getName() + " to: " + getOutputFile() + ". " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -62,6 +62,31 @@ public class ReadOps {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets all words as a ready-to-export CSV string. If the language is null or customWords is true,
|
||||
* only custom words are returned.
|
||||
*/
|
||||
@NonNull
|
||||
public String getWords(@NonNull SQLiteDatabase db, Language language, boolean customWords) {
|
||||
StringBuilder words = new StringBuilder();
|
||||
|
||||
String table = customWords || language == null ? Tables.CUSTOM_WORDS : Tables.getWords(language.getId());
|
||||
String[] columns = customWords || language == null ? new String[]{"word", "langId"} : new String[]{"word", "frequency"};
|
||||
|
||||
try (Cursor cursor = db.query(table, columns, null, null, null, null, null)) {
|
||||
while (cursor.moveToNext()) {
|
||||
words
|
||||
.append(cursor.getString(0))
|
||||
.append("\t")
|
||||
.append(cursor.getInt(1))
|
||||
.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
return words.toString();
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
public WordList getWords(@NonNull SQLiteDatabase db, @NonNull Language language, @NonNull String positions, String filter, int maximumWords, boolean fullOutput) {
|
||||
if (positions.isEmpty()) {
|
||||
|
|
@ -69,7 +94,6 @@ public class ReadOps {
|
|||
return new WordList();
|
||||
}
|
||||
|
||||
|
||||
String wordsQuery = getWordsQuery(language, positions, filter, maximumWords, fullOutput);
|
||||
if (wordsQuery.isEmpty()) {
|
||||
return new WordList();
|
||||
|
|
@ -139,7 +163,6 @@ public class ReadOps {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@NonNull private String getCustomWordPositions(@NonNull SQLiteDatabase db, Language language, String sequence, int generations) {
|
||||
try (Cursor cursor = db.rawQuery(getCustomWordPositionsQuery(language, sequence, generations), null)) {
|
||||
return new WordPositionsStringBuilder().appendFromDbRanges(cursor).toString();
|
||||
|
|
@ -156,7 +179,6 @@ public class ReadOps {
|
|||
}
|
||||
|
||||
|
||||
|
||||
@NonNull private String getFactoryWordPositionsQuery(@NonNull Language language, @NonNull String sequence, int generations) {
|
||||
StringBuilder sql = new StringBuilder("SELECT `start`, `end` FROM ")
|
||||
.append(Tables.getWordPositions(language.getId()))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.annotation.Nullable;
|
|||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
|
|
@ -16,7 +15,6 @@ import androidx.preference.PreferenceFragmentCompat;
|
|||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.DictionaryLoader;
|
||||
import io.github.sspanak.tt9.db.LegacyDb;
|
||||
import io.github.sspanak.tt9.db.WordStoreAsync;
|
||||
import io.github.sspanak.tt9.ime.helpers.InputModeValidator;
|
||||
|
|
@ -31,7 +29,6 @@ import io.github.sspanak.tt9.preferences.screens.KeyPadScreen;
|
|||
import io.github.sspanak.tt9.preferences.screens.MainSettingsScreen;
|
||||
import io.github.sspanak.tt9.preferences.screens.SetupScreen;
|
||||
import io.github.sspanak.tt9.preferences.screens.UsageStatsScreen;
|
||||
import io.github.sspanak.tt9.ui.DictionaryLoadingBar;
|
||||
|
||||
public class PreferencesActivity extends AppCompatActivity implements PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
|
||||
public SettingsStore settings;
|
||||
|
|
@ -176,14 +173,4 @@ public class PreferencesActivity extends AppCompatActivity implements Preference
|
|||
Hotkeys.setDefault(settings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public DictionaryLoadingBar getDictionaryProgressBar() {
|
||||
return DictionaryLoadingBar.getInstance(this);
|
||||
}
|
||||
|
||||
|
||||
public DictionaryLoader getDictionaryLoader() {
|
||||
return DictionaryLoader.getInstance(this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package io.github.sspanak.tt9.preferences.items;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
|
@ -12,14 +12,43 @@ abstract public class ItemClickable {
|
|||
private long lastClickTime = 0;
|
||||
|
||||
protected final Preference item;
|
||||
private final ArrayList<ItemClickable> otherItems = new ArrayList<>();
|
||||
|
||||
|
||||
public ItemClickable(Preference item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void disableAll(@NonNull ArrayList<ItemClickable> items) {
|
||||
for (ItemClickable i : items) {
|
||||
i.disable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void enableAll(@NonNull ArrayList<ItemClickable> items) {
|
||||
for (ItemClickable i : items) {
|
||||
i.enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void disableOthers(@NonNull ArrayList<ItemClickable> items, @NonNull ItemClickable exclude) {
|
||||
for (ItemClickable i : items) {
|
||||
if (i != exclude) {
|
||||
i.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void enableAllClickHandlers(@NonNull ArrayList<ItemClickable> items) {
|
||||
for (ItemClickable i : items) {
|
||||
i.enableClickHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void disable() {
|
||||
item.setEnabled(false);
|
||||
}
|
||||
|
|
@ -35,28 +64,6 @@ abstract public class ItemClickable {
|
|||
}
|
||||
|
||||
|
||||
public ItemClickable setOtherItems(List<ItemClickable> others) {
|
||||
otherItems.clear();
|
||||
otherItems.addAll(others);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
protected void disableOtherItems() {
|
||||
for (ItemClickable i : otherItems) {
|
||||
i.disable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected void enableOtherItems() {
|
||||
for (ItemClickable i : otherItems) {
|
||||
i.enable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean debounceClick(Preference p) {
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - lastClickTime < SettingsStore.PREFERENCES_CLICK_DEBOUNCE_TIME) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
package io.github.sspanak.tt9.preferences.items;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.exporter.AbstractExporter;
|
||||
import io.github.sspanak.tt9.ui.DictionaryNotification;
|
||||
|
||||
abstract public class ItemExportAbstract extends ItemClickable {
|
||||
final protected Activity activity;
|
||||
final private Runnable onStart;
|
||||
final private Runnable onFinish;
|
||||
|
||||
public ItemExportAbstract(Preference item, Activity activity, Runnable onStart, Runnable onFinish) {
|
||||
super(item);
|
||||
this.activity = activity;
|
||||
this.onStart = onStart;
|
||||
this.onFinish = onFinish;
|
||||
|
||||
AbstractExporter exporter = getExporter();
|
||||
exporter.setFailureHandler(() -> onFinishExporting(null));
|
||||
exporter.setSuccessHandler(this::onFinishExporting);
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
abstract protected AbstractExporter getExporter();
|
||||
|
||||
|
||||
public ItemExportAbstract refreshStatus() {
|
||||
if (item != null) {
|
||||
if (getExporter().isRunning()) {
|
||||
setLoadingStatus();
|
||||
} else {
|
||||
setReadyStatus();
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean onClick(Preference p) {
|
||||
setLoadingStatus();
|
||||
if (!onStartExporting()) {
|
||||
setReadyStatus();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
abstract protected boolean onStartExporting();
|
||||
|
||||
|
||||
protected void onFinishExporting(String outputFile) {
|
||||
activity.runOnUiThread(() -> {
|
||||
setReadyStatus();
|
||||
|
||||
if (outputFile == null) {
|
||||
DictionaryNotification.getInstance(activity).showError(
|
||||
activity.getString(R.string.dictionary_export_failed),
|
||||
activity.getString(R.string.dictionary_export_failed_more_info)
|
||||
);
|
||||
} else {
|
||||
DictionaryNotification.getInstance(activity).showMessage(
|
||||
activity.getString(R.string.dictionary_export_finished),
|
||||
activity.getString(R.string.dictionary_export_finished_more_info, outputFile),
|
||||
activity.getString(R.string.dictionary_export_finished_more_info, outputFile)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
abstract protected String getLoadingMessage();
|
||||
|
||||
|
||||
protected void setLoadingStatus() {
|
||||
onStart.run();
|
||||
disable();
|
||||
|
||||
String loadingMessage = getLoadingMessage();
|
||||
item.setSummary(loadingMessage);
|
||||
DictionaryNotification.getInstance(activity).showLoadingMessage(loadingMessage, "");
|
||||
}
|
||||
|
||||
|
||||
public void setReadyStatus() {
|
||||
enable();
|
||||
onFinish.run();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package io.github.sspanak.tt9.preferences.items;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.exporter.CustomWordsExporter;
|
||||
|
||||
public class ItemExportCustomWords extends ItemExportAbstract {
|
||||
final public static String NAME = "dictionary_export_custom";
|
||||
|
||||
|
||||
public ItemExportCustomWords(Preference item, Activity activity, Runnable onStart, Runnable onFinish) {
|
||||
super(item, activity, onStart, onFinish);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected CustomWordsExporter getExporter() {
|
||||
return CustomWordsExporter.getInstance();
|
||||
}
|
||||
|
||||
|
||||
protected boolean onStartExporting() {
|
||||
return CustomWordsExporter.getInstance().export(activity);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected String getLoadingMessage() {
|
||||
return activity.getString(R.string.dictionary_export_generating_csv);
|
||||
}
|
||||
|
||||
public void setReadyStatus() {
|
||||
super.setReadyStatus();
|
||||
item.setSummary(activity.getString(
|
||||
R.string.dictionary_export_custom_words_summary,
|
||||
CustomWordsExporter.getInstance().getOutputDir()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package io.github.sspanak.tt9.preferences.items;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import io.github.sspanak.tt9.Logger;
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.exporter.DictionaryExporter;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.preferences.SettingsStore;
|
||||
|
||||
public class ItemExportDictionary extends ItemExportAbstract {
|
||||
final public static String NAME = "dictionary_export";
|
||||
|
||||
protected final SettingsStore settings;
|
||||
|
||||
|
||||
public ItemExportDictionary(Preference item, Activity activity, SettingsStore settings, Runnable onStart, Runnable onFinish) {
|
||||
super(item, activity, onStart, onFinish);
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemExportAbstract refreshStatus() {
|
||||
if (item != null) {
|
||||
item.setVisible(Logger.isDebugLevel());
|
||||
}
|
||||
return super.refreshStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DictionaryExporter getExporter() {
|
||||
return DictionaryExporter.getInstance();
|
||||
}
|
||||
|
||||
|
||||
protected boolean onStartExporting() {
|
||||
return DictionaryExporter.getInstance()
|
||||
.setLanguages(LanguageCollection.getAll(activity, settings.getEnabledLanguageIds()))
|
||||
.export(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLoadingMessage() {
|
||||
String message = activity.getString(R.string.dictionary_export_generating_csv);
|
||||
|
||||
Language language = LanguageCollection.getLanguage(activity, settings.getInputLanguage());
|
||||
if (language != null) {
|
||||
message = activity.getString(R.string.dictionary_export_generating_csv_for_language, language.getName());
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
public void setReadyStatus() {
|
||||
super.setReadyStatus();
|
||||
item.setSummary("");
|
||||
}
|
||||
}
|
||||
|
|
@ -17,21 +17,27 @@ import io.github.sspanak.tt9.ui.UI;
|
|||
|
||||
|
||||
public class ItemLoadDictionary extends ItemClickable {
|
||||
public static final String NAME = "dictionary_load";
|
||||
public final static String NAME = "dictionary_load";
|
||||
|
||||
private final Context context;
|
||||
private final SettingsStore settings;
|
||||
private final Runnable onStart;
|
||||
private final Runnable onFinish;
|
||||
|
||||
private final DictionaryLoader loader;
|
||||
private final DictionaryLoadingBar progressBar;
|
||||
|
||||
|
||||
public ItemLoadDictionary(Preference item, Context context, SettingsStore settings, DictionaryLoader loader, DictionaryLoadingBar progressBar) {
|
||||
public ItemLoadDictionary(Preference item, Context context, SettingsStore settings, Runnable onStart, Runnable onFinish) {
|
||||
super(item);
|
||||
|
||||
this.context = context;
|
||||
this.loader = loader;
|
||||
this.progressBar = progressBar;
|
||||
this.loader = DictionaryLoader.getInstance(context);
|
||||
this.progressBar = DictionaryLoadingBar.getInstance(context);
|
||||
this.settings = settings;
|
||||
this.onStart = onStart;
|
||||
this.onFinish = onFinish;
|
||||
|
||||
|
||||
loader.setOnStatusChange(this::onLoadingStatusChange);
|
||||
refreshStatus();
|
||||
|
|
@ -56,7 +62,7 @@ public class ItemLoadDictionary extends ItemClickable {
|
|||
} else if (progressBar.isFailed()) {
|
||||
setReadyStatus();
|
||||
UI.toastFromAsync(context, progressBar.getMessage());
|
||||
} else if (progressBar.isCompleted()) {
|
||||
} else if (!progressBar.inProgress()) {
|
||||
setReadyStatus();
|
||||
UI.toastFromAsync(context, R.string.dictionary_loaded);
|
||||
}
|
||||
|
|
@ -78,13 +84,13 @@ public class ItemLoadDictionary extends ItemClickable {
|
|||
|
||||
|
||||
private void setLoadingStatus() {
|
||||
disableOtherItems();
|
||||
onStart.run();
|
||||
item.setTitle(context.getString(R.string.dictionary_cancel_load));
|
||||
}
|
||||
|
||||
|
||||
private void setReadyStatus() {
|
||||
enableOtherItems();
|
||||
onFinish.run();
|
||||
item.setTitle(context.getString(R.string.dictionary_load_title));
|
||||
item.setSummary(progressBar.isFailed() || progressBar.isCancelled() ? progressBar.getMessage() : "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import java.util.ArrayList;
|
|||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.WordStoreAsync;
|
||||
import io.github.sspanak.tt9.db.DictionaryLoader;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||
|
|
@ -17,22 +16,20 @@ public class ItemTruncateAll extends ItemClickable {
|
|||
public static final String NAME = "dictionary_truncate";
|
||||
|
||||
protected final PreferencesActivity activity;
|
||||
protected final DictionaryLoader loader;
|
||||
private final Runnable onStart;
|
||||
private final Runnable onFinish;
|
||||
|
||||
|
||||
public ItemTruncateAll(Preference item, PreferencesActivity activity, DictionaryLoader loader) {
|
||||
public ItemTruncateAll(Preference item, PreferencesActivity activity, Runnable onStart, Runnable onFinish) {
|
||||
super(item);
|
||||
this.activity = activity;
|
||||
this.loader = loader;
|
||||
this.onStart = onStart;
|
||||
this.onFinish = onFinish;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean onClick(Preference p) {
|
||||
if (loader != null && loader.isRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onStartDeleting();
|
||||
ArrayList<Integer> languageIds = new ArrayList<>();
|
||||
for (Language lang : LanguageCollection.getAll(activity, false)) {
|
||||
|
|
@ -45,7 +42,7 @@ public class ItemTruncateAll extends ItemClickable {
|
|||
|
||||
|
||||
protected void onStartDeleting() {
|
||||
disableOtherItems();
|
||||
onStart.run();
|
||||
disable();
|
||||
item.setSummary(R.string.dictionary_truncating);
|
||||
}
|
||||
|
|
@ -53,7 +50,7 @@ public class ItemTruncateAll extends ItemClickable {
|
|||
|
||||
protected void onFinishDeleting() {
|
||||
activity.runOnUiThread(() -> {
|
||||
enableOtherItems();
|
||||
onFinish.run();
|
||||
item.setSummary("");
|
||||
enable();
|
||||
UI.toastFromAsync(activity, R.string.dictionary_truncated);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.preference.Preference;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.db.WordStoreAsync;
|
||||
import io.github.sspanak.tt9.db.DictionaryLoader;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||
|
|
@ -18,19 +17,14 @@ public class ItemTruncateUnselected extends ItemTruncateAll {
|
|||
private final SettingsStore settings;
|
||||
|
||||
|
||||
public ItemTruncateUnselected(Preference item, PreferencesActivity context, SettingsStore settings, DictionaryLoader loader) {
|
||||
super(item, context, loader);
|
||||
public ItemTruncateUnselected(Preference item, PreferencesActivity context, SettingsStore settings, Runnable onStart, Runnable onFinish) {
|
||||
super(item, context, onStart, onFinish);
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean onClick(Preference p) {
|
||||
if (loader != null && loader.isRunning()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArrayList<Integer> unselectedLanguageIds = new ArrayList<>();
|
||||
ArrayList<Integer> selectedLanguageIds = settings.getEnabledLanguageIds();
|
||||
for (Language lang : LanguageCollection.getAll(activity, false)) {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,28 @@
|
|||
package io.github.sspanak.tt9.preferences.screens;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.db.DictionaryLoader;
|
||||
import io.github.sspanak.tt9.db.exporter.CustomWordsExporter;
|
||||
import io.github.sspanak.tt9.db.exporter.DictionaryExporter;
|
||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemClickable;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemExportCustomWords;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemExportDictionary;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemLoadDictionary;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemSelectLanguage;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemTruncateAll;
|
||||
import io.github.sspanak.tt9.preferences.items.ItemTruncateUnselected;
|
||||
|
||||
public class DictionariesScreen extends BaseScreenFragment {
|
||||
final public static String NAME = "Dictionaries";
|
||||
public static final String NAME = "Dictionaries";
|
||||
|
||||
private final ArrayList<ItemClickable> clickables = new ArrayList<>();
|
||||
|
||||
private ItemLoadDictionary loadItem;
|
||||
private ItemExportDictionary exportDictionaryItem;
|
||||
private ItemExportCustomWords exportCustomWordsItem;
|
||||
|
||||
public DictionariesScreen() { init(); }
|
||||
public DictionariesScreen(PreferencesActivity activity) { init(activity); }
|
||||
|
|
@ -30,35 +40,78 @@ public class DictionariesScreen extends BaseScreenFragment {
|
|||
);
|
||||
multiSelect.populate().enableValidation();
|
||||
|
||||
loadItem = new ItemLoadDictionary(
|
||||
findPreference(ItemLoadDictionary.NAME),
|
||||
loadItem = new ItemLoadDictionary(findPreference(ItemLoadDictionary.NAME),
|
||||
activity,
|
||||
activity.settings,
|
||||
activity.getDictionaryLoader(),
|
||||
activity.getDictionaryProgressBar()
|
||||
() -> ItemClickable.disableOthers(clickables, loadItem),
|
||||
this::onActionFinish
|
||||
);
|
||||
|
||||
ItemTruncateUnselected deleteItem = new ItemTruncateUnselected(
|
||||
exportDictionaryItem = new ItemExportDictionary(findPreference(ItemExportDictionary.NAME),
|
||||
activity,
|
||||
activity.settings,
|
||||
this::onActionStart,
|
||||
this::onActionFinish
|
||||
);
|
||||
|
||||
clickables.add(loadItem);
|
||||
clickables.add(exportDictionaryItem);
|
||||
|
||||
clickables.add(new ItemTruncateUnselected(
|
||||
findPreference(ItemTruncateUnselected.NAME),
|
||||
activity,
|
||||
activity.settings,
|
||||
activity.getDictionaryLoader()
|
||||
);
|
||||
this::onActionStart,
|
||||
this::onActionFinish
|
||||
));
|
||||
|
||||
ItemTruncateAll truncateItem = new ItemTruncateAll(
|
||||
clickables.add(new ItemTruncateAll(
|
||||
findPreference(ItemTruncateAll.NAME),
|
||||
activity,
|
||||
activity.getDictionaryLoader()
|
||||
);
|
||||
this::onActionStart,
|
||||
this::onActionFinish
|
||||
));
|
||||
|
||||
loadItem.setOtherItems(Arrays.asList(truncateItem, deleteItem)).enableClickHandler();
|
||||
deleteItem.setOtherItems(Arrays.asList(truncateItem, loadItem)).enableClickHandler();
|
||||
truncateItem.setOtherItems(Arrays.asList(deleteItem, loadItem)).enableClickHandler();
|
||||
exportCustomWordsItem = new ItemExportCustomWords(
|
||||
findPreference(ItemExportCustomWords.NAME),
|
||||
activity,
|
||||
this::onActionStart,
|
||||
this::onActionFinish);
|
||||
|
||||
clickables.add(exportCustomWordsItem);
|
||||
|
||||
ItemClickable.enableAllClickHandlers(clickables);
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
refreshItems();
|
||||
}
|
||||
|
||||
|
||||
private void refreshItems() {
|
||||
loadItem.refreshStatus();
|
||||
exportDictionaryItem.refreshStatus();
|
||||
exportCustomWordsItem.refreshStatus();
|
||||
|
||||
if (DictionaryLoader.getInstance(activity).isRunning()) {
|
||||
loadItem.refreshStatus();
|
||||
ItemClickable.disableOthers(clickables, loadItem);
|
||||
} else if (CustomWordsExporter.getInstance().isRunning() || DictionaryExporter.getInstance().isRunning()) {
|
||||
onActionStart();
|
||||
} else {
|
||||
onActionFinish();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void onActionStart() {
|
||||
ItemClickable.disableAll(clickables);
|
||||
}
|
||||
|
||||
private void onActionFinish() {
|
||||
ItemClickable.enableAll(clickables);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,8 @@
|
|||
package io.github.sspanak.tt9.ui;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
|
@ -21,28 +13,15 @@ import io.github.sspanak.tt9.languages.InvalidLanguageCharactersException;
|
|||
import io.github.sspanak.tt9.languages.InvalidLanguageException;
|
||||
import io.github.sspanak.tt9.languages.Language;
|
||||
import io.github.sspanak.tt9.languages.LanguageCollection;
|
||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||
import io.github.sspanak.tt9.preferences.screens.DictionariesScreen;
|
||||
|
||||
|
||||
public class DictionaryLoadingBar {
|
||||
public class DictionaryLoadingBar extends DictionaryNotification {
|
||||
private static DictionaryLoadingBar self;
|
||||
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "loading-notifications";
|
||||
|
||||
private final NotificationManager manager;
|
||||
private final NotificationCompat.Builder notificationBuilder;
|
||||
private final Resources resources;
|
||||
|
||||
private boolean isStopped = false;
|
||||
private boolean hasFailed = false;
|
||||
|
||||
private int maxProgress = 0;
|
||||
private int progress = 0;
|
||||
private String title = "";
|
||||
private String message = "";
|
||||
|
||||
|
||||
public static DictionaryLoadingBar getInstance(Context context) {
|
||||
if (self == null) {
|
||||
|
|
@ -54,39 +33,17 @@ public class DictionaryLoadingBar {
|
|||
|
||||
|
||||
private DictionaryLoadingBar(Context context) {
|
||||
resources = context.getResources();
|
||||
|
||||
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = getNotificationBuilderCompat(context);
|
||||
|
||||
notificationBuilder
|
||||
.setContentIntent(createNavigationIntent(context))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setOnlyAlertOnce(true);
|
||||
super(context);
|
||||
}
|
||||
|
||||
|
||||
private PendingIntent createNavigationIntent(Context context) {
|
||||
Intent intent = new Intent(context, PreferencesActivity.class);
|
||||
intent.putExtra("screen", DictionariesScreen.NAME);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return PendingIntent.getActivity(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
private NotificationCompat.Builder getNotificationBuilderCompat(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
manager.createNotificationChannel(new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
"Dictionary Status",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
));
|
||||
return new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
return new NotificationCompat.Builder(context);
|
||||
}
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -100,26 +57,11 @@ public class DictionaryLoadingBar {
|
|||
}
|
||||
|
||||
|
||||
public boolean isCompleted() {
|
||||
return progress >= maxProgress;
|
||||
}
|
||||
|
||||
|
||||
public boolean isFailed() {
|
||||
return hasFailed;
|
||||
}
|
||||
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
public void show(Context context, Bundle data) {
|
||||
String error = data.getString("error", null);
|
||||
int fileCount = data.getInt("fileCount", -1);
|
||||
|
|
@ -185,7 +127,7 @@ public class DictionaryLoadingBar {
|
|||
message = currentFileProgress + "%";
|
||||
}
|
||||
|
||||
renderProgress();
|
||||
renderMessage();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -207,37 +149,4 @@ public class DictionaryLoadingBar {
|
|||
|
||||
renderError();
|
||||
}
|
||||
|
||||
|
||||
private void hide() {
|
||||
progress = maxProgress = 0;
|
||||
manager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
|
||||
private void renderError() {
|
||||
NotificationCompat.BigTextStyle bigMessage = new NotificationCompat.BigTextStyle();
|
||||
bigMessage.setBigContentTitle(title);
|
||||
bigMessage.bigText(message);
|
||||
|
||||
notificationBuilder
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setStyle(bigMessage)
|
||||
.setOngoing(false)
|
||||
.setProgress(maxProgress, progress, false);
|
||||
|
||||
manager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
private void renderProgress() {
|
||||
notificationBuilder
|
||||
.setSmallIcon(isCompleted() ? R.drawable.ic_done : android.R.drawable.stat_notify_sync)
|
||||
.setOngoing(!isCompleted())
|
||||
.setProgress(maxProgress, progress, false)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message);
|
||||
|
||||
manager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,154 @@
|
|||
package io.github.sspanak.tt9.ui;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import io.github.sspanak.tt9.R;
|
||||
import io.github.sspanak.tt9.preferences.PreferencesActivity;
|
||||
import io.github.sspanak.tt9.preferences.screens.DictionariesScreen;
|
||||
|
||||
public abstract class DictionaryNotification {
|
||||
private static DictionaryNotification self;
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
private static final String NOTIFICATION_CHANNEL_ID = "dictionary-notifications";
|
||||
|
||||
private final NotificationManager manager;
|
||||
private final NotificationCompat.Builder notificationBuilder;
|
||||
protected final Resources resources;
|
||||
|
||||
protected int maxProgress = 0;
|
||||
protected int progress = 0;
|
||||
protected boolean indeterminate = false;
|
||||
protected String title = "";
|
||||
protected String message = "";
|
||||
protected String messageLong = "";
|
||||
|
||||
|
||||
protected DictionaryNotification(Context context) {
|
||||
resources = context.getResources();
|
||||
|
||||
manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationBuilder = getNotificationBuilderCompat(context);
|
||||
|
||||
notificationBuilder
|
||||
.setContentIntent(createNavigationIntent(context))
|
||||
.setSmallIcon(android.R.drawable.stat_notify_sync)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setOnlyAlertOnce(true);
|
||||
}
|
||||
|
||||
|
||||
public static DictionaryNotification getInstance(Context context) {
|
||||
if (self == null) {
|
||||
self = new DictionaryNotification(context) {
|
||||
};
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
private PendingIntent createNavigationIntent(Context context) {
|
||||
Intent intent = new Intent(context, PreferencesActivity.class);
|
||||
intent.putExtra("screen", DictionariesScreen.NAME);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
return PendingIntent.getActivity(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
}
|
||||
|
||||
|
||||
private NotificationCompat.Builder getNotificationBuilderCompat(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
manager.createNotificationChannel(new NotificationChannel(
|
||||
NOTIFICATION_CHANNEL_ID,
|
||||
"Dictionary Status",
|
||||
NotificationManager.IMPORTANCE_LOW
|
||||
));
|
||||
return new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID);
|
||||
} else {
|
||||
//noinspection deprecation
|
||||
return new NotificationCompat.Builder(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void showMessage(@NonNull String title, @NonNull String message, @NonNull String messageLong) {
|
||||
progress = maxProgress = 0;
|
||||
indeterminate = false;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.messageLong = messageLong;
|
||||
renderMessage();
|
||||
}
|
||||
|
||||
|
||||
public void showLoadingMessage(@NonNull String title, @NonNull String message) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
messageLong = "";
|
||||
indeterminate = true;
|
||||
progress = 1;
|
||||
maxProgress = 2;
|
||||
renderMessage();
|
||||
}
|
||||
|
||||
|
||||
public void showError(@NonNull String title, @NonNull String message) {
|
||||
progress = maxProgress = 0;
|
||||
indeterminate = false;
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
renderError();
|
||||
}
|
||||
|
||||
|
||||
protected void hide() {
|
||||
progress = maxProgress = 0;
|
||||
manager.cancel(NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
|
||||
public boolean inProgress() {
|
||||
return progress < maxProgress;
|
||||
}
|
||||
|
||||
|
||||
protected void renderError() {
|
||||
NotificationCompat.BigTextStyle bigMessage = new NotificationCompat.BigTextStyle();
|
||||
bigMessage.setBigContentTitle(title);
|
||||
bigMessage.bigText(message);
|
||||
|
||||
notificationBuilder
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message)
|
||||
.setOngoing(false)
|
||||
.setStyle(bigMessage)
|
||||
.setProgress(maxProgress, progress, false);
|
||||
|
||||
manager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
|
||||
|
||||
protected void renderMessage() {
|
||||
NotificationCompat.BigTextStyle bigMessage = new NotificationCompat.BigTextStyle();
|
||||
bigMessage.setBigContentTitle(title);
|
||||
bigMessage.bigText(messageLong.isEmpty() ? message : messageLong);
|
||||
|
||||
notificationBuilder
|
||||
.setSmallIcon(inProgress() ? android.R.drawable.stat_notify_sync : R.drawable.ic_done)
|
||||
.setOngoing(inProgress())
|
||||
.setProgress(maxProgress, progress, indeterminate)
|
||||
.setStyle(bigMessage)
|
||||
.setContentTitle(title)
|
||||
.setContentText(message);
|
||||
|
||||
manager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,11 @@
|
|||
<string name="pref_show_soft_function_keys">Бутони на екрана</string>
|
||||
<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_summary">Експортиране на CSV с всички добавени думи в: „%1$s“.</string>
|
||||
<string name="dictionary_export_failed">Неуспешно експортиране</string>
|
||||
<string name="dictionary_export_failed_more_info">За повече информация, активирайте режима за отстраняване на грешки и прегледайте журнала.</string>
|
||||
<string name="dictionary_update_message">Налично е обновление на речника за „%1$s“. Искате ли да го заредите?</string>
|
||||
<string name="dictionary_update_update">Зареди</string>
|
||||
<string name="donate_title">Дарете</string>
|
||||
|
|
@ -86,4 +91,8 @@
|
|||
<string name="add_word_confirm">Да се добави ли „%1$s“ към %2$s?</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Защита от случайно повтарящи бутони</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Изключена</string>
|
||||
<string name="dictionary_export_finished">Експортирането завърши</string>
|
||||
<string name="dictionary_export_finished_more_info">Думите са експортирани в: „%1$s“.</string>
|
||||
<string name="dictionary_export_generating_csv">Експортиране на CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Експортиране на CSV (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@
|
|||
<string name="pref_dark_theme_yes">Ja</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">" Wörterbuchupdate verfügbar für „%1$s“. Möchten Sie es laden?"</string>
|
||||
<string name="dictionary_update_update">Laden</string>
|
||||
<string name="donate_title">Spenden</string>
|
||||
|
|
@ -39,4 +44,8 @@
|
|||
<string name="pref_hack_google_chat">Nachrichten mit \"OK\" in Google Chat senden</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Schutz vor versehentlichem Tastenwiederholen</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Aus</string>
|
||||
<string name="dictionary_export_finished">Export abgeschlossen</string>
|
||||
<string name="dictionary_export_finished_more_info">Wörter exportiert nach: „%1$s“.</string>
|
||||
<string name="dictionary_export_generating_csv">CSV wird exportiert…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">CSV wird exportiert (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@
|
|||
<string name="pref_dark_theme_yes">Sí</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">Actualización del diccionario disponible para «%1$s». ¿Te gustaría cargarlo?</string>
|
||||
<string name="dictionary_update_update">Cargar</string>
|
||||
<string name="donate_title">Donar</string>
|
||||
|
|
@ -82,4 +87,8 @@
|
|||
<string name="pref_hack_google_chat">Enviar mensajes con «OK» en Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Protección contra la repetición accidental de teclas</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Apagado</string>
|
||||
<string name="dictionary_export_finished">" Exportación completada"</string>
|
||||
<string name="dictionary_export_finished_more_info">Palabras exportadas a: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">Exportando CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Exportando CSV (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -74,10 +74,19 @@
|
|||
<string name="pref_dark_theme_no">Non</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">Mise à jour du dictionnaire «%1$s» disponible. Souhaitez-vous le charger ?</string>
|
||||
<string name="dictionary_update_update">Charger</string>
|
||||
<string name="donate_title">Donner</string>
|
||||
<string name="donate_summary">Si vous aimez %1$s vous pouvez soutenir son développement à : %2$s</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Protection contre la répétition accidentelle des touches</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Désactivée</string>
|
||||
<string name="dictionary_export_finished">Exportation terminée</string>
|
||||
<string name="dictionary_export_finished_more_info">Mots exportés vers : «%1$s».</string>
|
||||
<string name="dictionary_export_generating_csv">Exportation CSV en cours…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Exportation CSV en cours (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -40,6 +40,11 @@
|
|||
<string name="pref_dark_theme_yes">Si</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">Aggiornamento del dizionario disponibile per \"%1$s\". Vuoi caricarlo?</string>
|
||||
<string name="dictionary_update_update">Carica</string>
|
||||
<string name="donate_title">Donare</string>
|
||||
|
|
@ -48,5 +53,9 @@
|
|||
<string name="pref_hack_google_chat">Inviare messaggi con \"OK\" su Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Protezione contro la ripetizione accidentale dei tasti</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Spento</string>
|
||||
<string name="dictionary_export_finished">Esportazione completata</string>
|
||||
<string name="dictionary_export_finished_more_info">Parole esportate su: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">CSV in corso…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">CSV in corso (%1$s)…</string>
|
||||
</resources>
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@
|
|||
<string name="pref_dark_theme_yes">כן</string>
|
||||
<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_summary">ייצוא CSV עם כל המילים שנוספו ל: \"%1$s\".</string>
|
||||
<string name="dictionary_export_failed">נכשל בייצוא</string>
|
||||
<string name="dictionary_export_failed_more_info">"למידע נוסף, הפעל מצב איתור באגים וראה את הלוגים. "</string>
|
||||
<string name="dictionary_update_message">עדכון מילון זמין עבור \"%1$s\". האם תרצה לטעון אותו?</string>
|
||||
<string name="dictionary_update_update">טען</string>
|
||||
<string name="donate_title">לִתְרוֹם</string>
|
||||
|
|
@ -75,4 +80,8 @@
|
|||
<string name="pref_hack_google_chat">שלח הודעות עם \"OK\" ב-Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">הגנה מפני חזרת מפתח בשוגג</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">כבוי</string>
|
||||
<string name="dictionary_export_finished">הייצוא הושלם</string>
|
||||
<string name="dictionary_export_finished_more_info">המילים יוצאות ל: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">מייצא CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">מייצא CSV (%1$s)...</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@
|
|||
<string name="pref_dark_theme_yes">Ja</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">Woordenboekupdate beschikbaar voor \"%1$s\". Wil je het laden?</string>
|
||||
<string name="dictionary_update_update">Laden</string>
|
||||
<string name="donate_title">Doneer</string>
|
||||
|
|
@ -39,4 +44,8 @@
|
|||
<string name="pref_hack_google_chat">Stuur berichten met \"OK\" in Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Bescherming tegen het per ongeluk herhalen van toetsen</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Uit</string>
|
||||
<string name="dictionary_export_finished">Export voltooid</string>
|
||||
<string name="dictionary_export_finished_more_info">Woorden geëxporteerd naar: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">CSV exporteren…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">CSV exporteren (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@
|
|||
<string name="pref_dark_theme_yes">Sim</string>
|
||||
<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_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>
|
||||
<string name="dictionary_update_message">Atualização do dicionário disponível para \"%1$s\". Você gostaria de carregá-lo?</string>
|
||||
<string name="dictionary_update_update">Carregar</string>
|
||||
<string name="donate_title">Doar</string>
|
||||
|
|
@ -71,4 +76,8 @@
|
|||
<string name="pref_hack_google_chat">Enviar mensagens com \"OK\" no Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Proteção contra repetição acidental de teclas</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Desligado</string>
|
||||
<string name="dictionary_export_finished">Exportação concluída</string>
|
||||
<string name="dictionary_export_finished_more_info">Palavras exportadas para: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">Exportando CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Exportando CSV (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@
|
|||
<string name="pref_dark_theme_no">Нет</string>
|
||||
<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_summary">Экспорт CSV со всеми добавленными словами в: «%1$s».</string>
|
||||
<string name="dictionary_export_failed">Ошибка экспорта</string>
|
||||
<string name="dictionary_export_failed_more_info">Для получения дополнительной информации включите режим отладки и просмотрите журналы.</string>
|
||||
<string name="dictionary_update_message">Доступно обновление словаря для «%1$s». Хотите загрузить его?</string>
|
||||
<string name="dictionary_update_update">Загрузить</string>
|
||||
<string name="donate_title">Поддержать</string>
|
||||
|
|
@ -86,4 +91,8 @@
|
|||
<string name="pref_hack_google_chat">Отправка сообщения с «ОК» в Google Chat</string>
|
||||
<string name="pref_hack_key_pad_debounce_time">Защита от случайного повторения нажатий</string>
|
||||
<string name="pref_hack_key_pad_debounce_off">Выключена</string>
|
||||
<string name="dictionary_export_finished">Экспорт завершен</string>
|
||||
<string name="dictionary_export_finished_more_info">Слова экспортированы в: «%1$s».</string>
|
||||
<string name="dictionary_export_generating_csv">Экспорт CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Экспорт CSV (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@
|
|||
<string name="dictionary_truncated">Словник видалено.</string>
|
||||
<string name="dictionary_truncating">Видаляється…</string>
|
||||
|
||||
<string name="dictionary_export">Експорт вибрані</string>
|
||||
<string name="dictionary_export_custom_words_button">Експортувати</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>
|
||||
<string name="dictionary_update_message">Доступне оновлення словника для \"%1$s\". Бажаєте його завантажити?</string>
|
||||
<string name="dictionary_update_update">Завантажити</string>
|
||||
|
||||
|
|
@ -97,4 +102,8 @@
|
|||
|
||||
<string name="char_newline">Новий рядок</string>
|
||||
<string name="char_space">Пробіл</string>
|
||||
<string name="dictionary_export_finished">Експорт завершено</string>
|
||||
<string name="dictionary_export_finished_more_info">Слова експортовані в: \" %1$s \".</string>
|
||||
<string name="dictionary_export_generating_csv">Експорт CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Експорт CSV (%1$s)…</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,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_hacks">Compatibility</string>
|
||||
<string name="pref_category_appearance">Appearance</string>
|
||||
<string name="pref_category_debug_options" translatable="false">Debug Options</string>
|
||||
|
|
@ -71,6 +72,16 @@
|
|||
<string name="dictionary_truncated">Dictionary successfully cleared.</string>
|
||||
<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_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>
|
||||
<string name="dictionary_export_finished">Exporting Finished</string>
|
||||
<string name="dictionary_export_finished_more_info">Words exported to: \"%1$s\".</string>
|
||||
<string name="dictionary_export_generating_csv">Exporting CSV…</string>
|
||||
<string name="dictionary_export_generating_csv_for_language">Exporting CSV (%1$s)…</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>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@
|
|||
app:layout="@layout/pref_text"
|
||||
app:title="@string/dictionary_load_title" />
|
||||
|
||||
<Preference
|
||||
app:key="dictionary_export"
|
||||
app:layout="@layout/pref_text"
|
||||
app:title="@string/dictionary_export"
|
||||
app:isPreferenceVisible="false" />
|
||||
|
||||
<Preference
|
||||
app:key="dictionary_truncate_unselected"
|
||||
app:layout="@layout/pref_text"
|
||||
|
|
@ -21,4 +27,16 @@
|
|||
app:layout="@layout/pref_text"
|
||||
app:title="@string/dictionary_truncate_title" />
|
||||
|
||||
<PreferenceCategory
|
||||
app:title="@string/pref_category_custom_words"
|
||||
app:layout="@layout/pref_category"
|
||||
app:singleLineTitle="true">
|
||||
|
||||
<Preference
|
||||
app:key="dictionary_export_custom"
|
||||
app:layout="@layout/pref_text"
|
||||
app:title="@string/dictionary_export_custom_words_button" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
</PreferenceScreen>
|
||||
|
|
|
|||
|
|
@ -10,28 +10,9 @@ _If you don't see the icon right after installing, restart your phone and it sho
|
|||
If your phone does not have a hardware keypad, check out the [On-screen Keypad section](#on-screen-keypad).
|
||||
|
||||
### Enabling Predictive Mode
|
||||
Predictive Mode requires a language dictionary to be loaded in order provide word suggestions. You can toggle the enabled languages and load their dictionaries from: [Settings screen](#settings-screen) → Languages.
|
||||
Predictive Mode requires a language dictionary to be loaded in order provide word suggestions. You can toggle the enabled languages and load their dictionaries from: Settings Screen → [Languages](#language-options). In case you have forgotten to load some dictionary, Traditional T9 will do it for you automatically, when you start typing.
|
||||
|
||||
In case you have forgotten to load some dictionary, Traditional T9 will do it for you automatically. Just go to some application where you can type and it will start loading. You will be prompted to wait until it completes and after that, you can start typing right away.
|
||||
|
||||
_If you [delete one or more dictionaries](#deleting-a-dictionary), they will NOT reload automatically. You will have to do so manually. Only dictionaries for newly enabled languages will load automatically._
|
||||
|
||||
### Dictionary Tips
|
||||
|
||||
#### Loading a Dictionary
|
||||
Once a dictionary is loaded, it will stay there until you use one of the "delete" options. This means you can enable and disable languages without reloading their dictionaries every time. Just do it once, only the first time.
|
||||
|
||||
It also means that if you need to start using language X, you can safely disable all other languages, load only dictionary X (and save time!), then re-enable all languages you used before.
|
||||
|
||||
Have in mind reloading a dictionary will reset the suggestion popularity to the factory defaults. However, there should be nothing to worry about. For the most part, you will see little to no difference in the suggestion order, unless you oftenly use uncommon words.
|
||||
|
||||
#### Deleting a Dictionary
|
||||
|
||||
If you have stopped using languages X or Y, you could disable them and also use "Delete Unselected", to free some storage space.
|
||||
|
||||
To delete everything, regardless of the selection, use "Delete All".
|
||||
|
||||
In all cases, your custom added words will be preserved and restored once you reload the respective dictionary.
|
||||
For more information, [see below](#language-options).
|
||||
|
||||
## Hotkeys
|
||||
|
||||
|
|
@ -156,6 +137,33 @@ Click on the Traditional T9 launcher icon.
|
|||
|
||||
_The actual menu names may vary depending on your phone, Android version and language._
|
||||
|
||||
### Language Options
|
||||
|
||||
#### Loading a Dictionary
|
||||
After enabling one or more new languages, you must load the respective dictionaries for Predictive Mode. Once a dictionary is loaded, it will stay there until you use one of the "delete" options. This means you can enable and disable languages without reloading their dictionaries every time. Just do it once, only the first time.
|
||||
|
||||
It also means that if you need to start using language X, you can safely disable all other languages, load only dictionary X (and save time!), then re-enable all languages you used before.
|
||||
|
||||
Have in mind reloading a dictionary will reset the suggestion popularity to the factory defaults. However, there should be nothing to worry about. For the most part, you will see little to no difference in the suggestion order, unless you oftenly use uncommon words.
|
||||
|
||||
#### Automatic Dictionary Loading
|
||||
|
||||
If you skip or forget to load a dictionary from the Settings screen, it will happen automatically later, when you go to an application where you can type, and switch to Predictive Mode. You will be prompted to wait until it completes and after that, you can start typing right away.
|
||||
|
||||
If you delete one or more dictionaries, they will NOT reload automatically. You will have to do so manually. Only dictionaries for newly enabled languages will load automatically.
|
||||
|
||||
#### Deleting a Dictionary
|
||||
If you have stopped using languages X or Y, you could disable them and also use "Delete Unselected", to free some storage space.
|
||||
|
||||
To delete everything, regardless of the selection, use "Delete All".
|
||||
|
||||
In all cases, your custom added words will be preserved and restored once you reload the respective dictionary.
|
||||
|
||||
#### Added Words
|
||||
The "Export" option allows you to export all added words, for all languages, including any added emoji, to a CSV file. Then, you can use the CSV file to make Traditional T9 better! Go to Github and share the words in a [new issue](https://github.com/sspanak/tt9/issues) or [pull request](https://github.com/sspanak/tt9/issues). After being reviewed and approved, they will be included in the next version.
|
||||
|
||||
Using "Delete", you can search for and delete misspelled words or others that you don't want in the dictionary.
|
||||
|
||||
### Compatibility Options & Troubleshooting
|
||||
For a number of applications or devices, it is possible to enable special options, which will make Traditional T9 work better with them. You can find them in: Settings → Initial Setup, under the Compatibility section.
|
||||
|
||||
|
|
@ -165,7 +173,7 @@ On some devices, in Predictive Mode, you may not be able to see all suggestions,
|
|||
#### Key repeat protection
|
||||
CAT S22 Flip and Qin F21 phones are known for their low quality keypads, which degrade quickly over time, and start registering multiple clicks for a single key press. You may notice this when typing or navigating the phone menus.
|
||||
|
||||
For CAT phones the recommended setting is 75-100 ms. For Qin F21, try with 20-30 ms. If you are still experiencing the issue, increase the value a bit, but generally try to keep it as low as possible.
|
||||
For CAT phones the recommended setting is 50-75 ms. For Qin F21, try with 20-30 ms. If you are still experiencing the issue, increase the value a bit, but generally try to keep it as low as possible.
|
||||
|
||||
_**Note:** The higher value you set, the slower you will have to type. TT9 will ignore very quick key presses._
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue