diff --git a/res/layout/pref_plain_text.xml b/res/layout/pref_plain_text.xml new file mode 100644 index 00000000..6b11f6e9 --- /dev/null +++ b/res/layout/pref_plain_text.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index d6b98f98..69c49835 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -21,12 +21,13 @@ About ABC Mode Appearance + Debug Options + Recent Log Messages Predictive Mode Select Hotkeys Keypad Initial Setup - Automatic Letter Select Automatically type the selected letter after a short delay. Automatic Space @@ -38,6 +39,8 @@ Yes No Auto + Detailed Debug Logs + System Logs Character for Double 0-key Press Show On-Screen Keys Show On-Screen Numpad diff --git a/res/xml/prefs.xml b/res/xml/prefs.xml index 9a15698e..ea43f8ba 100644 --- a/res/xml/prefs.xml +++ b/res/xml/prefs.xml @@ -44,6 +44,7 @@ app:singleLineTitle="true"> diff --git a/res/xml/prefs_screen_debug.xml b/res/xml/prefs_screen_debug.xml new file mode 100644 index 00000000..98136b3d --- /dev/null +++ b/res/xml/prefs_screen_debug.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/src/io/github/sspanak/tt9/Logger.java b/src/io/github/sspanak/tt9/Logger.java index b19cbd0f..37ecad97 100644 --- a/src/io/github/sspanak/tt9/Logger.java +++ b/src/io/github/sspanak/tt9/Logger.java @@ -3,39 +3,48 @@ package io.github.sspanak.tt9; import android.util.Log; public class Logger { - public static final int LEVEL = BuildConfig.DEBUG ? Log.DEBUG : Log.ERROR; + public static final String TAG_PREFIX = "tt9/"; + public static int LEVEL = BuildConfig.DEBUG ? Log.DEBUG : Log.ERROR; - static public boolean isDebugLevel() { + public static boolean isDebugLevel() { return LEVEL == Log.DEBUG; } + public static void setDebugLevel() { + LEVEL = Log.DEBUG; + } + + public static void setDefaultLevel() { + LEVEL = Log.ERROR; + } + static public void v(String tag, String msg) { if (LEVEL <= Log.VERBOSE) { - Log.v(tag, msg); + Log.v(TAG_PREFIX + tag, msg); } } static public void d(String tag, String msg) { if (LEVEL <= Log.DEBUG) { - Log.d(tag, msg); + Log.d(TAG_PREFIX + tag, msg); } } static public void i(String tag, String msg) { if (LEVEL <= Log.INFO) { - Log.i(tag, msg); + Log.i(TAG_PREFIX + tag, msg); } } static public void w(String tag, String msg) { if (LEVEL <= Log.WARN) { - Log.w(tag, msg); + Log.w(TAG_PREFIX + tag, msg); } } static public void e(String tag, String msg) { if (LEVEL <= Log.ERROR) { - Log.e(tag, msg); + Log.e(TAG_PREFIX + tag, msg); } } } diff --git a/src/io/github/sspanak/tt9/db/DictionaryDb.java b/src/io/github/sspanak/tt9/db/DictionaryDb.java index 0eea12c9..4424d6dc 100644 --- a/src/io/github/sspanak/tt9/db/DictionaryDb.java +++ b/src/io/github/sspanak/tt9/db/DictionaryDb.java @@ -146,11 +146,11 @@ public class DictionaryDb { } catch (SQLiteConstraintException e) { String msg = "Constraint violation when inserting a word: '" + dbWord.word + "' / sequence: '" + dbWord.sequence + "', for language: " + dbWord.langId + ". " + e.getMessage(); - Logger.e("tt9/insertWord", msg); + Logger.e("insertWord", msg); statusHandler.accept(1); } catch (Exception e) { String msg = "Failed inserting word: '" + dbWord.word + "' / sequence: '" + dbWord.sequence + "', for language: " + dbWord.langId + ". " + e.getMessage(); - Logger.e("tt9/insertWord", msg); + Logger.e("insertWord", msg); statusHandler.accept(2); } }).start(); @@ -270,13 +270,13 @@ public class DictionaryDb { ArrayList wordList = new ArrayList<>(maxWords); if (sequence == null || sequence.length() == 0) { - Logger.w("tt9/db.getWords", "Attempting to get words for an empty sequence."); + Logger.w("db.getWords", "Attempting to get words for an empty sequence."); sendWords(dataHandler, wordList); return; } if (language == null) { - Logger.w("tt9/db.getWords", "Attempting to get words for NULL language."); + Logger.w("db.getWords", "Attempting to get words for NULL language."); sendWords(dataHandler, wordList); return; } diff --git a/src/io/github/sspanak/tt9/db/DictionaryLoader.java b/src/io/github/sspanak/tt9/db/DictionaryLoader.java index fb063bfc..eaa9cc1b 100644 --- a/src/io/github/sspanak/tt9/db/DictionaryLoader.java +++ b/src/io/github/sspanak/tt9/db/DictionaryLoader.java @@ -294,7 +294,7 @@ public class DictionaryLoader { private void sendFileCount(int fileCount) { if (onStatusChange == null) { Logger.w( - "tt9/DictionaryLoader.sendFileCount", + "DictionaryLoader.sendFileCount", "Cannot send file count without a status Handler. Ignoring message."); return; } @@ -308,7 +308,7 @@ public class DictionaryLoader { private void sendProgressMessage(Language language, int progress, int progressUpdateInterval) { if (onStatusChange == null) { Logger.w( - "tt9/DictionaryLoader.sendProgressMessage", + "DictionaryLoader.sendProgressMessage", "Cannot send progress without a status Handler. Ignoring message."); return; } @@ -331,7 +331,7 @@ public class DictionaryLoader { private void sendError(String message, int langId) { if (onStatusChange == null) { - Logger.w("tt9/DictionaryLoader.sendError", "Cannot send an error without a status Handler. Ignoring message."); + Logger.w("DictionaryLoader.sendError", "Cannot send an error without a status Handler. Ignoring message."); return; } @@ -344,7 +344,7 @@ public class DictionaryLoader { private void sendImportError(String message, int langId, long fileLine, String word) { if (onStatusChange == null) { - Logger.w("tt9/DictionaryLoader.sendError", "Cannot send an import error without a status Handler. Ignoring message."); + Logger.w("DictionaryLoader.sendError", "Cannot send an import error without a status Handler. Ignoring message."); return; } diff --git a/src/io/github/sspanak/tt9/ime/TraditionalT9.java b/src/io/github/sspanak/tt9/ime/TraditionalT9.java index 47616585..d436e4eb 100644 --- a/src/io/github/sspanak/tt9/ime/TraditionalT9.java +++ b/src/io/github/sspanak/tt9/ime/TraditionalT9.java @@ -751,7 +751,7 @@ public class TraditionalT9 extends KeyPadHandler { textField.setText(word); mInputMode.reset(); } catch (Exception e) { - Logger.w("tt9/restoreLastWord", "Could not restore the last added word. " + e.getMessage()); + Logger.w("restoreLastWord", "Could not restore the last added word. " + e.getMessage()); } } diff --git a/src/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java b/src/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java index 55381f61..0447a64e 100644 --- a/src/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java +++ b/src/io/github/sspanak/tt9/ime/helpers/InputModeValidator.java @@ -18,7 +18,7 @@ public class InputModeValidator { } if (validLanguageIds.size() == 0) { validLanguageIds.add(LanguageCollection.getDefault(context).getId()); - Logger.e("tt9/validateEnabledLanguages", "The language list seems to be corrupted. Resetting to first language only."); + Logger.e("validateEnabledLanguages", "The language list seems to be corrupted. Resetting to first language only."); } return validLanguageIds; @@ -34,7 +34,7 @@ public class InputModeValidator { Language validLanguage = LanguageCollection.getLanguage(context, validLanguageIds.get(0)); validLanguage = validLanguage != null ? validLanguage : LanguageCollection.getDefault(context); - Logger.w("tt9/validateLanguage", error + " Enforcing language: " + validLanguage.getId()); + Logger.w("validateLanguage", error + " Enforcing language: " + validLanguage.getId()); return validLanguage; } @@ -51,7 +51,7 @@ public class InputModeValidator { } if (newModeId != oldModeId) { - Logger.w("tt9/validateMode", "Invalid input mode: " + oldModeId + " Enforcing: " + newModeId); + Logger.w("validateMode", "Invalid input mode: " + oldModeId + " Enforcing: " + newModeId); } return newModeId; @@ -60,7 +60,7 @@ public class InputModeValidator { public static void validateTextCase(InputMode inputMode, int newTextCase) { if (!inputMode.setTextCase(newTextCase)) { inputMode.defaultTextCase(); - Logger.w("tt9/validateTextCase", "Invalid text case: " + newTextCase + " Enforcing: " + inputMode.getTextCase()); + Logger.w("validateTextCase", "Invalid text case: " + newTextCase + " Enforcing: " + inputMode.getTextCase()); } } } diff --git a/src/io/github/sspanak/tt9/ime/modes/InputMode.java b/src/io/github/sspanak/tt9/ime/modes/InputMode.java index e77cbaa0..6639e593 100644 --- a/src/io/github/sspanak/tt9/ime/modes/InputMode.java +++ b/src/io/github/sspanak/tt9/ime/modes/InputMode.java @@ -43,7 +43,7 @@ abstract public class InputMode { case MODE_PASSTHROUGH: return new ModePassthrough(); default: - Logger.w("tt9/InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode); + Logger.w("InputMode", "Defaulting to mode: " + Mode123.class.getName() + " for unknown InputMode: " + mode); case MODE_123: return new Mode123(); } diff --git a/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java b/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java index 35d803a4..58178ca7 100644 --- a/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java +++ b/src/io/github/sspanak/tt9/ime/modes/ModePredictive.java @@ -147,7 +147,7 @@ public class ModePredictive extends InputMode { } stem = ""; - Logger.d("tt9/setWordStem", "Stem filter cleared"); + Logger.d("setWordStem", "Stem filter cleared"); return true; } @@ -182,13 +182,13 @@ public class ModePredictive extends InputMode { stem = sanitizedStem.toLowerCase(language.getLocale()); isStemFuzzy = !exact; - Logger.d("tt9/setWordStem", "Stem is now: " + stem + (isStemFuzzy ? " (fuzzy)" : "")); + Logger.d("setWordStem", "Stem is now: " + stem + (isStemFuzzy ? " (fuzzy)" : "")); return true; } catch (Exception e) { isStemFuzzy = false; stem = ""; - Logger.w("tt9/setWordStem", "Ignoring invalid stem: " + newStem + ". " + e.getMessage()); + Logger.w("setWordStem", "Ignoring invalid stem: " + newStem + ". " + e.getMessage()); return false; } } @@ -284,7 +284,7 @@ public class ModePredictive extends InputMode { DictionaryDb.incrementWordFrequency(language, currentWord, sequence); } } catch (Exception e) { - Logger.e("tt9/ModePredictive", "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage()); + Logger.e("ModePredictive", "Failed incrementing priority of word: '" + currentWord + "'. " + e.getMessage()); } } diff --git a/src/io/github/sspanak/tt9/preferences/PreferencesActivity.java b/src/io/github/sspanak/tt9/preferences/PreferencesActivity.java index 6031bddd..8aa7db43 100644 --- a/src/io/github/sspanak/tt9/preferences/PreferencesActivity.java +++ b/src/io/github/sspanak/tt9/preferences/PreferencesActivity.java @@ -20,6 +20,7 @@ import io.github.sspanak.tt9.ime.helpers.GlobalKeyboardSettings; import io.github.sspanak.tt9.ime.helpers.InputModeValidator; import io.github.sspanak.tt9.preferences.helpers.Hotkeys; import io.github.sspanak.tt9.preferences.screens.AppearanceScreen; +import io.github.sspanak.tt9.preferences.screens.DebugScreen; import io.github.sspanak.tt9.preferences.screens.DictionariesScreen; import io.github.sspanak.tt9.preferences.screens.HotkeysScreen; import io.github.sspanak.tt9.preferences.screens.KeyPadScreen; @@ -84,6 +85,8 @@ public class PreferencesActivity extends AppCompatActivity implements Preference switch (name) { case "Appearance": return new AppearanceScreen(this); + case "Debug": + return new DebugScreen(this); case "Dictionaries": return new DictionariesScreen(this); case "Hotkeys": diff --git a/src/io/github/sspanak/tt9/preferences/SettingsStore.java b/src/io/github/sspanak/tt9/preferences/SettingsStore.java index 8bfccfbf..55bff22c 100644 --- a/src/io/github/sspanak/tt9/preferences/SettingsStore.java +++ b/src/io/github/sspanak/tt9/preferences/SettingsStore.java @@ -91,7 +91,7 @@ public class SettingsStore { Set validLanguageIds = new HashSet<>(); for (String langId : languageIds) { - if (!validateSavedLanguage(Integer.parseInt(langId), "tt9/saveEnabledLanguageIds")){ + if (!validateSavedLanguage(Integer.parseInt(langId), "saveEnabledLanguageIds")){ continue; } @@ -99,7 +99,7 @@ public class SettingsStore { } if (validLanguageIds.size() == 0) { - Logger.w("tt9/saveEnabledLanguageIds", "Refusing to save an empty language list"); + Logger.w("saveEnabledLanguageIds", "Refusing to save an empty language list"); return; } @@ -116,7 +116,7 @@ public class SettingsStore { boolean isTextCaseValid = isIntInList( textCase, new ArrayList<>(Arrays.asList(InputMode.CASE_CAPITALIZE, InputMode.CASE_LOWER, InputMode.CASE_UPPER)), - "tt9/saveTextCase", + "saveTextCase", "Not saving invalid text case: " + textCase ); @@ -132,7 +132,7 @@ public class SettingsStore { } public void saveInputLanguage(int language) { - if (validateSavedLanguage(language, "tt9/saveInputLanguage")){ + if (validateSavedLanguage(language, "saveInputLanguage")){ prefsEditor.putInt("pref_input_language", language); prefsEditor.apply(); } @@ -147,7 +147,7 @@ public class SettingsStore { boolean isModeValid = isIntInList( mode, new ArrayList<>(Arrays.asList(InputMode.MODE_123, InputMode.MODE_PREDICTIVE, InputMode.MODE_ABC)), - "tt9/saveInputMode", + "saveInputMode", "Not saving invalid input mode: " + mode ); diff --git a/src/io/github/sspanak/tt9/preferences/items/ItemDropDown.java b/src/io/github/sspanak/tt9/preferences/items/ItemDropDown.java index 64568bf3..e95e9353 100644 --- a/src/io/github/sspanak/tt9/preferences/items/ItemDropDown.java +++ b/src/io/github/sspanak/tt9/preferences/items/ItemDropDown.java @@ -18,7 +18,7 @@ public class ItemDropDown { protected void populate(LinkedHashMap values) { if (item == null) { - Logger.w("tt9/ItemDropDown.populate", "Cannot populate a NULL item. Ignoring."); + Logger.w("ItemDropDown.populate", "Cannot populate a NULL item. Ignoring."); return; } @@ -35,7 +35,7 @@ public class ItemDropDown { public ItemDropDown enableClickHandler() { if (item == null) { - Logger.w("tt9/SectionKeymap.populateItem", "Cannot set a click listener a NULL item. Ignoring."); + Logger.w("SectionKeymap.populateItem", "Cannot set a click listener a NULL item. Ignoring."); return this; } diff --git a/src/io/github/sspanak/tt9/preferences/items/ItemSelectZeroKeyCharacter.java b/src/io/github/sspanak/tt9/preferences/items/ItemSelectZeroKeyCharacter.java index c44d70ff..4a9b8f93 100644 --- a/src/io/github/sspanak/tt9/preferences/items/ItemSelectZeroKeyCharacter.java +++ b/src/io/github/sspanak/tt9/preferences/items/ItemSelectZeroKeyCharacter.java @@ -30,7 +30,7 @@ public class ItemSelectZeroKeyCharacter { public ItemSelectZeroKeyCharacter populate() { if (item == null) { - Logger.w("tt9/ItemSelectZeroKeyChar.populate", "Cannot populate a NULL item. Ignoring."); + Logger.w("ItemSelectZeroKeyChar.populate", "Cannot populate a NULL item. Ignoring."); return this; } @@ -44,7 +44,7 @@ public class ItemSelectZeroKeyCharacter { public void activate() { if (item == null) { - Logger.w("tt9/ItemSelectZeroKeyChar.activate", "Cannot set a click listener a NULL item. Ignoring."); + Logger.w("ItemSelectZeroKeyChar.activate", "Cannot set a click listener a NULL item. Ignoring."); return; } diff --git a/src/io/github/sspanak/tt9/preferences/items/SectionKeymap.java b/src/io/github/sspanak/tt9/preferences/items/SectionKeymap.java index 75339693..7dd7d209 100644 --- a/src/io/github/sspanak/tt9/preferences/items/SectionKeymap.java +++ b/src/io/github/sspanak/tt9/preferences/items/SectionKeymap.java @@ -70,7 +70,7 @@ public class SectionKeymap { private void populateItem(DropDownPreference dropDown) { if (dropDown == null) { - Logger.w("tt9/SectionKeymap.populateItem", "Cannot populate a NULL item. Ignoring."); + Logger.w("SectionKeymap.populateItem", "Cannot populate a NULL item. Ignoring."); return; } @@ -98,7 +98,7 @@ public class SectionKeymap { private void onItemClick(DropDownPreference item) { if (item == null) { - Logger.w("tt9/SectionKeymap.populateItem", "Cannot set a click listener a NULL item. Ignoring."); + Logger.w("SectionKeymap.populateItem", "Cannot set a click listener a NULL item. Ignoring."); return; } @@ -140,7 +140,7 @@ public class SectionKeymap { for (DropDownPreference item : items) { if (item != null && !dropDown.getKey().equals(item.getKey()) && key.equals(item.getValue())) { - Logger.i("tt9/SectionKeymap.validateKey", "Key: '" + key + "' is already in use for function: " + item.getKey()); + Logger.i("SectionKeymap.validateKey", "Key: '" + key + "' is already in use for function: " + item.getKey()); return false; } } diff --git a/src/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java b/src/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java index 62f25821..a8620f8c 100644 --- a/src/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java +++ b/src/io/github/sspanak/tt9/preferences/screens/BaseScreenFragment.java @@ -41,7 +41,7 @@ abstract class BaseScreenFragment extends PreferenceFragmentCompat { if (activity == null) { Logger.w( - "tt9/MainSettingsScreen", + "MainSettingsScreen", "Starting up without an Activity. Preference Items will not be fully initialized." ); return; diff --git a/src/io/github/sspanak/tt9/preferences/screens/DebugScreen.java b/src/io/github/sspanak/tt9/preferences/screens/DebugScreen.java new file mode 100644 index 00000000..ff79b220 --- /dev/null +++ b/src/io/github/sspanak/tt9/preferences/screens/DebugScreen.java @@ -0,0 +1,95 @@ +package io.github.sspanak.tt9.preferences.screens; + +import androidx.preference.Preference; +import androidx.preference.SwitchPreferenceCompat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import io.github.sspanak.tt9.Logger; +import io.github.sspanak.tt9.R; +import io.github.sspanak.tt9.preferences.PreferencesActivity; + +public class DebugScreen extends BaseScreenFragment { + private static final String DEBUG_LOGS_SWITCH = "pref_enable_debug_logs"; + private static final String SYSTEM_LOGS_SWITCH = "pref_enable_system_logs"; + private static final String LOGS_CONTAINER = "debug_logs_container"; + + public DebugScreen() { init(); } + public DebugScreen(PreferencesActivity activity) { init(activity); } + + @Override protected int getTitle() { return R.string.pref_category_debug_options; } + @Override protected int getXml() { return R.xml.prefs_screen_debug; } + + @Override + protected void onCreate() { + initLogMessagesSwitch(); + initSystemLogsSwitch(); + + SwitchPreferenceCompat systemLogs = findPreference(SYSTEM_LOGS_SWITCH); + boolean includeSystemLogs = systemLogs != null && systemLogs.isChecked(); + printLogs(includeSystemLogs); + } + + private void initLogMessagesSwitch() { + SwitchPreferenceCompat msgSwitch = findPreference(DEBUG_LOGS_SWITCH); + if (msgSwitch == null) { + Logger.w("DebugScreen", "Debug logs switch not found."); + return; + } + + msgSwitch.setChecked(Logger.isDebugLevel()); + msgSwitch.setOnPreferenceChangeListener((Preference p, Object newValue) -> { + if ((boolean) newValue) { + Logger.setDebugLevel(); + } else { + Logger.setDefaultLevel(); + } + return true; + }); + } + + private void initSystemLogsSwitch() { + SwitchPreferenceCompat systemLogs = findPreference(SYSTEM_LOGS_SWITCH); + if (systemLogs == null) { + Logger.w("DebugScreen", "System logs switch not found."); + return; + } + + systemLogs.setOnPreferenceChangeListener((p, newValue) -> { + printLogs((boolean) newValue); + return true; + }); + } + + private void printLogs(boolean includeSystemLogs) { + Preference logsContainer = findPreference(LOGS_CONTAINER); + if (logsContainer == null) { + Logger.w("DebugScreen", "Logs container not found. Cannot print logs"); + return; + } + + StringBuilder log = new StringBuilder(); + try { + Process process = Runtime.getRuntime().exec("logcat -d -v threadtime io.github.sspanak.tt9:D"); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + + String line; + while ((line = bufferedReader.readLine()) != null) { + if (includeSystemLogs || line.contains(Logger.TAG_PREFIX)) { + log.append(line).append("\n\n"); + } + } + } + catch (IOException e) { + log.append("Error getting the logs. ").append(e.getMessage()); + } + + if (log.toString().isEmpty()) { + log.append("No Logs"); + } + + logsContainer.setSummary(log.toString()); + } +} diff --git a/src/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java b/src/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java index 57df9e5d..61a99812 100644 --- a/src/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java +++ b/src/io/github/sspanak/tt9/preferences/screens/MainSettingsScreen.java @@ -59,7 +59,7 @@ public class MainSettingsScreen extends BaseScreenFragment { intent.setData(Uri.parse(versionedHelpUrl)); helpSection.setIntent(intent); } catch (Exception e) { - Logger.w("tt9/MainSettingsScreen", "Could not set versioned help URL. Falling back to the default. " + e.getMessage()); + Logger.w("MainSettingsScreen", "Could not set versioned help URL. Falling back to the default. " + e.getMessage()); } }