From 846187d321a5bb3e8e054004d2ad78a8c10eb670 Mon Sep 17 00:00:00 2001 From: Francesco <franzbianconero@gmail.com> Date: Sat, 23 Dec 2017 10:13:16 +0100 Subject: [PATCH] 6.4 --- app/build.gradle | 7 +- app/proguard-rules.pro | 9 +- app/src/main/AndroidManifest.xml | 2 +- .../consolelauncher/LauncherActivity.java | 101 +- .../andre/consolelauncher/MainManager.java | 120 +- .../ohi/andre/consolelauncher/UIManager.java | 116 +- .../consolelauncher/commands/Command.java | 12 +- .../commands/CommandAbstraction.java | 2 - .../commands/CommandTuils.java | 9 +- .../commands/CommandsPreferences.java | 29 +- .../commands/main/MainPack.java | 7 +- .../commands/main/raw/airplane.java | 5 - .../commands/main/raw/apps.java | 14 +- .../commands/main/raw/beep.java | 5 - .../commands/main/raw/bluetooth.java | 5 - .../commands/main/raw/calc.java | 5 - .../commands/main/raw/call.java | 5 - .../commands/main/raw/clear.java | 5 - .../commands/main/raw/config.java | 39 +- .../commands/main/raw/ctrlc.java | 8 +- .../commands/main/raw/data.java | 5 - .../commands/main/raw/donate.java | 5 - .../commands/main/raw/exit.java | 5 - .../commands/main/raw/flash.java | 5 - .../commands/main/raw/help.java | 5 - .../commands/main/raw/location.java | 5 - .../commands/main/raw/notifications.java | 89 +- .../commands/main/raw/open.java | 5 - .../commands/main/raw/rate.java | 32 +- .../commands/main/raw/refresh.java | 5 - .../commands/main/raw/regex.java | 6 +- .../commands/main/raw/restart.java | 5 - .../commands/main/raw/rss.java | 101 +- .../commands/main/raw/search.java | 6 +- .../commands/main/raw/share.java | 5 - .../commands/main/raw/shellcommands.java | 5 - .../commands/main/raw/sms.java | 5 - .../commands/main/raw/status.java | 5 - .../commands/main/raw/time.java | 7 +- .../commands/main/raw/tuixt.java | 5 - .../commands/main/raw/tutorial.java | 5 - .../commands/main/raw/uninstall.java | 5 - .../commands/main/raw/vibrate.java | 5 - .../commands/main/raw/wifi.java | 5 - .../commands/specific/ParamCommand.java | 18 +- .../commands/tuixt/TuixtActivity.java | 3 +- .../commands/tuixt/raw/exit.java | 5 - .../commands/tuixt/raw/help.java | 5 - .../commands/tuixt/raw/save.java | 5 - .../managers/AliasManager.java | 41 +- .../consolelauncher/managers/AppsManager.java | 127 +- .../managers/ContactManager.java | 3 +- .../managers/MessagesManager.java | 6 +- .../managers/RegexManager.java | 120 +- .../consolelauncher/managers/RssManager.java | 829 ++++-- .../managers/TerminalManager.java | 119 +- .../managers/ThemesManager.java | 2 + .../{tuils => managers}/TimeManager.java | 53 +- .../managers/music/MusicManager2.java | 3 +- .../managers/notifications/KeeperService.java | 220 ++ .../notifications/NotificationManager.java | 277 +- .../notifications/NotificationService.java | 392 ++- .../suggestions/SuggestionsManager.java | 310 +-- .../managers/xml/XMLPrefsManager.java | 345 ++- .../managers/xml/options/Apps.java | 30 + .../managers/xml/options/Behavior.java | 580 ++++- .../managers/xml/options/Cmd.java | 10 + .../managers/xml/options/Notifications.java | 59 +- .../managers/xml/options/Rss.java | 125 +- .../managers/xml/options/Suggestions.java | 135 + .../managers/xml/options/Theme.java | 128 +- .../managers/xml/options/Toolbar.java | 10 + .../managers/xml/options/Ui.java | 444 +++- .../tuils/AllowEqualsSequence.java | 4 +- .../tuils/InputOutputReceiver.java | 50 +- .../consolelauncher/tuils/KeeperService.java | 72 - .../tuils/LongClickMovementMethod.java | 139 + .../tuils/LongClickableSpan.java | 125 + .../tuils/StoppableThread.java | 12 +- .../andre/consolelauncher/tuils/Tuils.java | 211 +- .../Html4EscapeSymbolsInitializer.java | 356 +++ .../Html5EscapeSymbolsInitializer.java | 2320 +++++++++++++++++ .../tuils/html_escape/HtmlEscape.java | 1283 +++++++++ .../tuils/html_escape/HtmlEscapeLevel.java | 138 + .../tuils/html_escape/HtmlEscapeSymbols.java | 573 ++++ .../tuils/html_escape/HtmlEscapeType.java | 119 + .../tuils/html_escape/HtmlEscapeUtil.java | 1368 ++++++++++ .../tuils/interfaces/CommandExecuter.java | 7 +- app/src/main/res/menu/notification_menu.xml | 5 + app/src/main/res/values/strings.xml | 45 +- build.gradle | 1 + 91 files changed, 10554 insertions(+), 1514 deletions(-) rename app/src/main/java/ohi/andre/consolelauncher/{tuils => managers}/TimeManager.java (68%) create mode 100755 app/src/main/java/ohi/andre/consolelauncher/managers/notifications/KeeperService.java delete mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/KeeperService.java create mode 100644 app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickMovementMethod.java create mode 100644 app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickableSpan.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html4EscapeSymbolsInitializer.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html5EscapeSymbolsInitializer.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscape.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeLevel.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeSymbols.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeType.java create mode 100755 app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeUtil.java create mode 100644 app/src/main/res/menu/notification_menu.xml diff --git a/app/build.gradle b/app/build.gradle index cd8655d..991a1d7 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { minSdkVersion 9 targetSdkVersion 23 - versionCode 137 - versionName "6.3k" + versionCode 150 + versionName "6.4" } buildTypes { @@ -40,7 +40,9 @@ android { applicationVariants.all { variant -> def vn = variant.versionName + def x = vn.substring(0, vn.length() - 1) + if (x.endsWith(".")) x = vn variant.outputs.each { output -> output.outputFile = new File( @@ -56,4 +58,5 @@ android { } dependencies { compile 'com.google.firebase:firebase-database:9.0.0' + compile 'com.squareup.okhttp3:okhttp:3.9.0' } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2cc184a..3d30a1d 100755 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -3,7 +3,7 @@ -keep public class ohi.andre.consolelauncher.commands.tuixt.raw.* { public *; } -keep public class ohi.andre.consolelauncher.managers.notifications.NotificationService --keep public class ohi.andre.consolelauncher.tuils.KeeperService +-keep public class ohi.andre.consolelauncher.managers.notifications.KeeperService -keep public class ohi.andre.consolelauncher.managers.options.** @@ -11,4 +11,9 @@ -dontwarn javax.annotation.** -dontwarn javax.inject.** --dontwarn sun.misc.Unsafe \ No newline at end of file +-dontwarn sun.misc.Unsafe + +-dontwarn okhttp3.** +-dontwarn okio.** +-dontwarn javax.annotation.** +-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8c33c70..9b35847 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -114,7 +114,7 @@ </receiver> <service - android:name=".tuils.KeeperService" + android:name=".managers.notifications.KeeperService" android:enabled="true" android:exported="false" /> diff --git a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java index e282eab..b114ee4 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java +++ b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java @@ -6,7 +6,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -28,7 +27,10 @@ import java.util.Queue; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.tuixt.TuixtActivity; import ohi.andre.consolelauncher.managers.ContactManager; +import ohi.andre.consolelauncher.managers.RegexManager; import ohi.andre.consolelauncher.managers.TerminalManager; +import ohi.andre.consolelauncher.managers.TimeManager; +import ohi.andre.consolelauncher.managers.notifications.KeeperService; import ohi.andre.consolelauncher.managers.notifications.NotificationManager; import ohi.andre.consolelauncher.managers.notifications.NotificationMonitorService; import ohi.andre.consolelauncher.managers.notifications.NotificationService; @@ -40,9 +42,8 @@ import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.managers.xml.options.Ui; import ohi.andre.consolelauncher.tuils.Assist; import ohi.andre.consolelauncher.tuils.InputOutputReceiver; -import ohi.andre.consolelauncher.tuils.KeeperService; +import ohi.andre.consolelauncher.tuils.LongClickableSpan; import ohi.andre.consolelauncher.tuils.SimpleMutableEntry; -import ohi.andre.consolelauncher.tuils.TimeManager; import ohi.andre.consolelauncher.tuils.Tuils; import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter; import ohi.andre.consolelauncher.tuils.interfaces.Inputable; @@ -53,6 +54,7 @@ import ohi.andre.consolelauncher.tuils.interfaces.Suggester; public class LauncherActivity extends AppCompatActivity implements Reloadable { private final String FIRSTACCESS_KEY = "x3"; + private final String NEED_RESET_TIME = "t0"; public static final int COMMAND_REQUEST_PERMISSION = 10; public static final int STARTING_PERMISSION = 11; @@ -71,28 +73,34 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { @Override public void run() { finish(); + + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startActivity(startMain); } }; private CommandExecuter ex = new CommandExecuter() { @Override - public String exec(String cmd, String aliasName) { + public void exec(String cmd, String aliasName) { if(main != null) main.onCommand(cmd, aliasName); - return null; } @Override - public String exec(String input) { + public void exec(String input) { exec(input, false); - return null; } @Override - public String exec(String input, boolean needWriteInput) { + public void exec(String input, boolean needWriteInput) { if(ui != null && needWriteInput) ui.setOutput(input, TerminalManager.CATEGORY_INPUT); if(main != null) main.onCommand(input, null); - return null; + } + + @Override + public void exec(String input, Object obj) { + if(main != null) main.onCommand(input, obj); } }; @@ -200,7 +208,7 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { @Override public void dispose() { - handler.removeCallbacksAndMessages(null); + if(handler != null) handler.removeCallbacksAndMessages(null); } }; @@ -234,23 +242,35 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { private void finishOnCreate() { -// Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { -// @Override -// public void uncaughtException(Thread t, Throwable e) { -// Tuils.toFile(e); -// } -// }); + RegexManager.create(this); + + Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + Tuils.toFile(e); + Tuils.log(e); + System.exit(1); + } + }); IntentFilter filter = new IntentFilter(); filter.addAction(InputOutputReceiver.ACTION_CMD); filter.addAction(InputOutputReceiver.ACTION_OUTPUT); + filter.addAction(InputOutputReceiver.ACTION_INPUT); - ioReceiver = new InputOutputReceiver(ex, out); + ioReceiver = new InputOutputReceiver(ex, out, in); getApplicationContext().registerReceiver(ioReceiver, filter); try { XMLPrefsManager.create(this); - TimeManager.create(); + new Thread() { + @Override + public void run() { + super.run(); + + TimeManager.create(); + } + }.start(); } catch (Exception e) { Tuils.log(Tuils.getStackTrace(e)); Tuils.toFile(e); @@ -269,6 +289,7 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { boolean showNotification = XMLPrefsManager.getBoolean(Behavior.tui_notification); Intent keeperIntent = new Intent(this, KeeperService.class); if (showNotification) { + keeperIntent.putExtra(KeeperService.PATH_KEY, XMLPrefsManager.get(Behavior.home_path)); startService(keeperIntent); } else { try { @@ -299,10 +320,7 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { Tuils.toFile(e); } - - boolean notifications = XMLPrefsManager.getBoolean(Notifications.show_notifications) || - XMLPrefsManager.get(Notifications.show_notifications).equalsIgnoreCase("enabled"); - + boolean notifications = XMLPrefsManager.getBoolean(Notifications.show_notifications) || XMLPrefsManager.get(Notifications.show_notifications).equalsIgnoreCase("enabled"); if(notifications) { try { ComponentName thisComponent = new ComponentName(this, NotificationService.class); @@ -330,6 +348,8 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { } } + LongClickableSpan.longPressVibrateDuration = XMLPrefsManager.getInt(Behavior.long_click_vibration_duration); + openKeyboardOnStart = XMLPrefsManager.getBoolean(Behavior.auto_show_keyboard); if (!openKeyboardOnStart) { this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); @@ -339,7 +359,7 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { ViewGroup mainView = (ViewGroup) findViewById(R.id.mainview); main = new MainManager(this, in, out, sugg, ex); - ui = new UIManager(main.getMainPack(), this, mainView, ex, main.getMainPack(), canApplyTheme); + ui = new UIManager(this, mainView, ex, main.getMainPack(), canApplyTheme); main.setRedirectionListener(ui.buildRedirectionListener()); main.setHintable(ui.getHintable()); main.setRooter(ui.getRooter()); @@ -350,16 +370,25 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { if(fullscreen) Assist.assistActivity(this); SharedPreferences preferences = getPreferences(0); + SharedPreferences.Editor editor = preferences.edit(); + boolean firstAccess = preferences.getBoolean(FIRSTACCESS_KEY, true); if (firstAccess) { - SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean(FIRSTACCESS_KEY, false); - editor.commit(); ui.setOutput(getString(R.string.firsthelp_text), TerminalManager.CATEGORY_OUTPUT); ui.setInput("tutorial"); } + boolean needResetTime = preferences.getBoolean(NEED_RESET_TIME, true); + if(needResetTime) { + editor.putBoolean(NEED_RESET_TIME, false); + + Behavior.time_format.parent().write(Behavior.time_format, Behavior.time_format.defaultValue()); + } + + editor.apply(); + System.gc(); } @@ -392,9 +421,9 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { super.onDestroy(); try { - stopService(new Intent(this, KeeperService.class)); - stopService(new Intent(this, NotificationMonitorService.class)); getApplicationContext().unregisterReceiver(ioReceiver); + stopService(new Intent(this, NotificationMonitorService.class)); + stopService(new Intent(this, KeeperService.class)); } catch (NoClassDefFoundError | Exception e) {} overridePendingTransition(0,0); @@ -431,10 +460,6 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { public void run() { super.run(); - try { - sleep(1000); - } catch (InterruptedException e) {} - runOnUiThread(stopActivity); } }.start(); @@ -546,9 +571,15 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable { } @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - - if(ui != null) ui.scrollToEnd(); + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + String cmd = intent.getStringExtra(InputOutputReceiver.TEXT); + if(cmd != null) { + Intent i = new Intent(InputOutputReceiver.ACTION_CMD); + i.putExtra(InputOutputReceiver.TEXT, cmd); + i.putExtra(InputOutputReceiver.SHOW_CONTENT, true); + sendBroadcast(i); + } } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java index 8a890cf..92c1908 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java @@ -22,13 +22,13 @@ import ohi.andre.consolelauncher.managers.AppsManager; import ohi.andre.consolelauncher.managers.ContactManager; import ohi.andre.consolelauncher.managers.RssManager; import ohi.andre.consolelauncher.managers.TerminalManager; +import ohi.andre.consolelauncher.managers.TimeManager; import ohi.andre.consolelauncher.managers.music.MusicManager2; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Behavior; import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.tuils.Compare; import ohi.andre.consolelauncher.tuils.StoppableThread; -import ohi.andre.consolelauncher.tuils.TimeManager; import ohi.andre.consolelauncher.tuils.Tuils; import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter; import ohi.andre.consolelauncher.tuils.interfaces.Hintable; @@ -40,6 +40,7 @@ import ohi.andre.consolelauncher.tuils.interfaces.Rooter; import ohi.andre.consolelauncher.tuils.interfaces.Suggester; import ohi.andre.consolelauncher.tuils.libsuperuser.Shell; import ohi.andre.consolelauncher.tuils.libsuperuser.ShellHolder; +import okhttp3.OkHttpClient; /*Copyright Francesco Andreuzzi @@ -94,7 +95,7 @@ public class MainManager { new AliasTrigger(), new TuiCommandTrigger(), new AppTrigger(), - new SystemCommandTrigger() + new ShellCommandTrigger() }; private MainPack mainPack; @@ -140,9 +141,23 @@ public class MainManager { ShellHolder shellHolder = new ShellHolder(out); interactive = shellHolder.build(); - RssManager rss = new RssManager(mContext); + OkHttpClient client = new OkHttpClient(); + RssManager rss = new RssManager(mContext, client); - mainPack = new MainPack(mContext, group, aliasManager, appsMgr, music, cont, c, executer, redirectator, shellHolder, rss); + mainPack = new MainPack(mContext, group, aliasManager, appsMgr, music, cont, c, executer, redirectator, shellHolder, rss, client); + } + + public void onCommand(String input, Object obj) { + if(obj == null || !(obj instanceof AppsManager.LaunchInfo)) { + onCommand(input, null); + return; + } + + if(obj instanceof AppsManager.LaunchInfo && ((AppsManager.LaunchInfo) obj).publicLabel.equals(input)) { + performLaunch((AppsManager.LaunchInfo) obj); + } else { + onCommand(input, null); + } } // command manager @@ -202,13 +217,18 @@ public class MainManager { public void destroy() { mainPack.destroy(); - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); - interactive.kill(); - interactive.close(); + try { + interactive.kill(); + interactive.close(); + } catch (Exception e) { + Tuils.log(e); + Tuils.toFile(e); + } } }.start(); } @@ -225,6 +245,47 @@ public class MainManager { this.mainPack.rooter = rooter; } +// + String appFormat; + int timeColor; + int outputColor; + + Pattern pa = Pattern.compile("%a", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + Pattern pp = Pattern.compile("%p", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + Pattern pl = Pattern.compile("%l", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + Pattern pn = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + + public boolean performLaunch(AppsManager.LaunchInfo i) { + Intent intent = mainPack.appsManager.getIntent(i); + if (intent == null) { + return false; + } + + if(showAppHistory) { + if(appFormat == null) { + appFormat = XMLPrefsManager.get(Behavior.app_launch_format); + timeColor = XMLPrefsManager.getColor(Theme.time_color); + outputColor = XMLPrefsManager.getColor(Theme.output_color); + } + + String a = new String(appFormat); + a = pa.matcher(a).replaceAll(Matcher.quoteReplacement(intent.getComponent().getClassName())); + a = pp.matcher(a).replaceAll(Matcher.quoteReplacement(intent.getComponent().getPackageName())); + a = pl.matcher(a).replaceAll(Matcher.quoteReplacement(i.publicLabel)); + a = pn.matcher(a).replaceAll(Matcher.quoteReplacement(Tuils.NEWLINE)); + + SpannableString text = new SpannableString(a); + text.setSpan(new ForegroundColorSpan(outputColor), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + CharSequence s = TimeManager.replace(text, timeColor); + + out.onOutput(s, TerminalManager.CATEGORY_OUTPUT); + } + + mContext.startActivity(intent); + return true; + } +// + interface CmdTrigger { boolean trigger(ExecutePack info, String input) throws Exception; } @@ -284,7 +345,7 @@ public class MainManager { } } - private class SystemCommandTrigger implements CmdTrigger { + private class ShellCommandTrigger implements CmdTrigger { final int CD_CODE = 10; final int PWD_CODE = 11; @@ -314,7 +375,7 @@ public class MainManager { @Override public boolean trigger(final ExecutePack info, final String input) throws Exception { - new Thread() { + new StoppableThread() { @Override public void run() { if(input.trim().equalsIgnoreCase("su")) { @@ -336,49 +397,10 @@ public class MainManager { private class AppTrigger implements CmdTrigger { - String appFormat; - int timeColor; - int outputColor; - - Pattern pa = Pattern.compile("%a", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - Pattern pp = Pattern.compile("%p", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - Pattern pl = Pattern.compile("%l", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - Pattern pn = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - @Override public boolean trigger(ExecutePack info, String input) { AppsManager.LaunchInfo i = mainPack.appsManager.findLaunchInfoWithLabel(input, AppsManager.SHOWN_APPS); - if (i == null) { - return false; - } - - Intent intent = mainPack.appsManager.getIntent(i); - if (intent == null) { - return false; - } - - if(showAppHistory) { - if(appFormat == null) { - appFormat = XMLPrefsManager.get(Behavior.app_launch_format); - timeColor = XMLPrefsManager.getColor(Theme.time_color); - outputColor = XMLPrefsManager.getColor(Theme.output_color); - } - - String a = new String(appFormat); - a = pa.matcher(a).replaceAll(Matcher.quoteReplacement(intent.getComponent().getClassName())); - a = pp.matcher(a).replaceAll(Matcher.quoteReplacement(intent.getComponent().getPackageName())); - a = pl.matcher(a).replaceAll(Matcher.quoteReplacement(i.publicLabel)); - a = pn.matcher(a).replaceAll(Matcher.quoteReplacement(Tuils.NEWLINE)); - - SpannableString text = new SpannableString(a); - text.setSpan(new ForegroundColorSpan(outputColor), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - CharSequence s = TimeManager.replace(text, timeColor); - - out.onOutput(s, TerminalManager.CATEGORY_GENERAL); - } - - mContext.startActivity(intent); - return true; + return i != null && performLaunch(i); } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java index 8f9e4b9..c620d4b 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java @@ -41,11 +41,11 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.specific.RedirectCommand; import ohi.andre.consolelauncher.managers.MessagesManager; import ohi.andre.consolelauncher.managers.TerminalManager; +import ohi.andre.consolelauncher.managers.TimeManager; import ohi.andre.consolelauncher.managers.suggestions.SuggestionRunnable; import ohi.andre.consolelauncher.managers.suggestions.SuggestionsManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; @@ -57,7 +57,6 @@ import ohi.andre.consolelauncher.managers.xml.options.Ui; import ohi.andre.consolelauncher.tuils.AllowEqualsSequence; import ohi.andre.consolelauncher.tuils.NetworkUtils; import ohi.andre.consolelauncher.tuils.StoppableThread; -import ohi.andre.consolelauncher.tuils.TimeManager; import ohi.andre.consolelauncher.tuils.Tuils; import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter; import ohi.andre.consolelauncher.tuils.interfaces.Hintable; @@ -104,21 +103,14 @@ public class UIManager implements OnTouchListener { private String multipleCmdSeparator; -// private boolean selectFirstSuggestionEnter = false; private OnNewInputListener inputListener = new OnNewInputListener() { @Override - public void onNewInput(String input) { + public void onNewInput(String input, Object obj) { if(suggestionsView != null) { -// if(suggestionsView.getChildCount() > 0 && selectFirstSuggestionEnter) { -// View v = suggestionsView.getChildAt(0); -// v.performClick(); -// return; -// } suggestionsView.removeAllViews(); - } - trigger.exec(input); + trigger.exec(input, obj); } }; @@ -435,6 +427,7 @@ public class UIManager implements OnTouchListener { final Pattern nl = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); final Pattern ip4 = Pattern.compile("%ip4", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); final Pattern ip6 = Pattern.compile("%ip6", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + final Pattern dt = Pattern.compile("%dt", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); final Pattern optionalWifi = Pattern.compile("%\\(([^/]*)/([^)]*)\\)", Pattern.CASE_INSENSITIVE); final Pattern optionalData = Pattern.compile("%\\[([^/]*)/([^\\]]*)\\]", Pattern.CASE_INSENSITIVE); @@ -459,7 +452,7 @@ public class UIManager implements OnTouchListener { if(format == null) { format = XMLPrefsManager.get(Behavior.network_info_format); color = XMLPrefsManager.getColor(Theme.network_info_color); - maxDepth = XMLPrefsManager.getInt(Behavior.max_optional_depth_network_info); + maxDepth = XMLPrefsManager.getInt(Behavior.max_optional_depth); updateTime = XMLPrefsManager.getInt(Behavior.network_info_update_ms); if(updateTime < 1000) updateTime = Integer.parseInt(Behavior.network_info_update_ms.defaultValue()); @@ -478,12 +471,6 @@ public class UIManager implements OnTouchListener { } } -// mobile data - boolean mobileOn = false; - try { - mobileOn = method != null && connectivityManager != null && (Boolean) method.invoke(connectivityManager); - } catch (Exception e) {} - // wifi boolean wifiOn = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected(); String wifiName = null; @@ -494,6 +481,19 @@ public class UIManager implements OnTouchListener { } } +// mobile data + boolean mobileOn = false; + try { + mobileOn = method != null && connectivityManager != null && (Boolean) method.invoke(connectivityManager); + } catch (Exception e) {} + + String mobileType = null; + if(mobileOn) { + mobileType = Tuils.getNetworkType(mContext); + } else { + mobileType = "unknown"; + } + // bluetooth boolean bluetoothOn = mBluetoothAdapter != null && mBluetoothAdapter.isEnabled(); @@ -523,6 +523,7 @@ public class UIManager implements OnTouchListener { copy = b4.matcher(copy).replaceAll(bluetoothOn ? TRUE : FALSE); copy = ip4.matcher(copy).replaceAll(NetworkUtils.getIPAddress(true)); copy = ip6.matcher(copy).replaceAll(NetworkUtils.getIPAddress(false)); + copy = dt.matcher(copy).replaceAll(mobileType); copy = nl.matcher(copy).replaceAll(Tuils.NEWLINE); int i = Label.network.ordinal(); @@ -648,10 +649,12 @@ public class UIManager implements OnTouchListener { if(suggestion.type == SuggestionsManager.Suggestion.TYPE_PERMANENT) { mTerminalAdapter.setInput(input + text); } else { + boolean addSpace = suggestion.type != SuggestionsManager.Suggestion.TYPE_FILE && suggestion.type != SuggestionsManager.Suggestion.TYPE_COLOR; + if(multipleCmdSeparator.length() > 0) { String[] split = input.split(multipleCmdSeparator); if(split.length == 0) return; - if(split.length == 1) mTerminalAdapter.setInput(text + (suggestion.type == SuggestionsManager.Suggestion.TYPE_FILE ? Tuils.EMPTYSTRING : Tuils.SPACE)); + if(split.length == 1) mTerminalAdapter.setInput(text + (addSpace ? Tuils.SPACE : Tuils.EMPTYSTRING), suggestion.object); else { split[split.length - 1] = Tuils.EMPTYSTRING; @@ -660,10 +663,10 @@ public class UIManager implements OnTouchListener { beforeInputs = beforeInputs + split[count] + multipleCmdSeparator; } - mTerminalAdapter.setInput(beforeInputs + text + (suggestion.type == SuggestionsManager.Suggestion.TYPE_FILE ? Tuils.EMPTYSTRING : Tuils.SPACE)); + mTerminalAdapter.setInput(beforeInputs + text + (addSpace ? Tuils.SPACE : Tuils.EMPTYSTRING), suggestion.object); } } else { - mTerminalAdapter.setInput(text + (suggestion.type == SuggestionsManager.Suggestion.TYPE_FILE ? Tuils.EMPTYSTRING : Tuils.SPACE)); + mTerminalAdapter.setInput(text + (addSpace ? Tuils.SPACE : Tuils.EMPTYSTRING), suggestion.object); } } @@ -790,7 +793,7 @@ public class UIManager implements OnTouchListener { } catch (InternalError e) {} } - protected UIManager(ExecutePack info, final Context context, final ViewGroup rootView, final CommandExecuter tri, MainPack mainPack, boolean canApplyTheme) { + protected UIManager(final Context context, final ViewGroup rootView, final CommandExecuter tri, MainPack mainPack, boolean canApplyTheme) { policy = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); component = new ComponentName(context, PolicyReceiver.class); @@ -799,7 +802,7 @@ public class UIManager implements OnTouchListener { // selectFirstSuggestionEnter = XMLPrefsManager.get(boolean.class, XMLPrefsManager.Behavior.enter_first_suggestion); mContext = context; - this.info = (MainPack) info; + this.info = mainPack; trigger = tri; @@ -863,12 +866,12 @@ public class UIManager implements OnTouchListener { float nIndex = showNetwork ? XMLPrefsManager.getFloat(Ui.network_index) : Integer.MAX_VALUE; int[] pos = { - XMLPrefsManager.getInt(Ui.status_line0_position), - XMLPrefsManager.getInt(Ui.status_line1_position), - XMLPrefsManager.getInt(Ui.status_line2_position), - XMLPrefsManager.getInt(Ui.status_line3_position), - XMLPrefsManager.getInt(Ui.status_line4_position), - XMLPrefsManager.getInt(Ui.status_line5_position) + XMLPrefsManager.getInt(Ui.status_line0_alignment), + XMLPrefsManager.getInt(Ui.status_line1_alignment), + XMLPrefsManager.getInt(Ui.status_line2_alignment), + XMLPrefsManager.getInt(Ui.status_line3_alignment), + XMLPrefsManager.getInt(Ui.status_line4_alignment), + XMLPrefsManager.getInt(Ui.status_line5_alignment) }; AllowEqualsSequence sequence = new AllowEqualsSequence(new float[] {rIndex, dIndex, bIndex, tIndex, sIndex, nIndex}, @@ -890,6 +893,7 @@ public class UIManager implements OnTouchListener { if(count >= sequence.getMinKey() && count <= sequence.getMaxKey() && os.length > 0) { labelViews[count].setTypeface(Tuils.getTypeface(context)); +// -1 = left 0 = center 1 = right int p = pos[count]; if(p >= 0) labelViews[count].setGravity(p == 0 ? Gravity.CENTER_HORIZONTAL : Gravity.RIGHT); } else { @@ -1049,36 +1053,38 @@ public class UIManager implements OnTouchListener { if(XMLPrefsManager.getBoolean(Behavior.show_hints)) { MessagesManager messagesManager = new MessagesManager(context, - new MessagesManager.Message(context.getString(R.string.hint_alias)), - new MessagesManager.Message(context.getString(R.string.hint_appgroups)), - new MessagesManager.Message(context.getString(R.string.hint_clear)), - new MessagesManager.Message(context.getString(R.string.hint_config)), - new MessagesManager.Message(context.getString(R.string.hint_disable)), - new MessagesManager.Message(context.getString(R.string.hint_donate)), - new MessagesManager.Message(context.getString(R.string.hint_googlep)), - new MessagesManager.Message(context.getString(R.string.hint_help)), - new MessagesManager.Message(context.getString(R.string.hint_music)), - new MessagesManager.Message(context.getString(R.string.hint_notifications)), - new MessagesManager.Message(context.getString(R.string.hint_telegram)), - new MessagesManager.Message(context.getString(R.string.hint_theme)), - new MessagesManager.Message(context.getString(R.string.hint_theme2)), - new MessagesManager.Message(context.getString(R.string.hint_tutorial)), - new MessagesManager.Message(context.getString(R.string.hint_twitter)), - new MessagesManager.Message(context.getString(R.string.hint_wallpaper)), - new MessagesManager.Message(context.getString(R.string.hint_musicdisable))); + new MessagesManager.Message(R.string.hint_alias), + new MessagesManager.Message(R.string.hint_appgroups), + new MessagesManager.Message(R.string.hint_clear), + new MessagesManager.Message(R.string.hint_config), + new MessagesManager.Message(R.string.hint_disable), + new MessagesManager.Message(R.string.hint_donate), + new MessagesManager.Message(R.string.hint_googlep), + new MessagesManager.Message(R.string.hint_help), + new MessagesManager.Message(R.string.hint_music), + new MessagesManager.Message(R.string.hint_notifications), + new MessagesManager.Message(R.string.hint_telegram), + new MessagesManager.Message(R.string.hint_theme), + new MessagesManager.Message(R.string.hint_theme2), + new MessagesManager.Message(R.string.hint_tutorial), + new MessagesManager.Message(R.string.hint_twitter), + new MessagesManager.Message(R.string.hint_wallpaper), + new MessagesManager.Message(R.string.hint_musicdisable), + new MessagesManager.Message(R.string.hint_excludenotification) + ); mTerminalAdapter.setMessagesManager(messagesManager); } } public void dispose() { - handler.removeCallbacksAndMessages(null); + if(handler != null) handler.removeCallbacksAndMessages(null); } public void openKeyboard() { mTerminalAdapter.requestInputFocus(); imm.showSoftInput(mTerminalAdapter.getInputView(), InputMethodManager.SHOW_IMPLICIT); - mTerminalAdapter.scrollToEnd(); +// mTerminalAdapter.scrollToEnd(); } public void closeKeyboard() { @@ -1135,10 +1141,6 @@ public class UIManager implements OnTouchListener { mTerminalAdapter.requestInputFocus(); } - public void scrollToEnd() { - mTerminalAdapter.scrollToEnd(); - } - public Hintable getHintable() { return new Hintable() { @Override @@ -1245,13 +1247,13 @@ public class UIManager implements OnTouchListener { }; } - public interface SuggestionNavigator { - boolean isNavigating(); - void onEnter(); - } +// public interface SuggestionNavigator { +// boolean isNavigating(); +// void onEnter(); +// } public interface OnNewInputListener { - void onNewInput(String input); + void onNewInput(String input, Object obj); } // public class PowerConnectionReceiver extends BroadcastReceiver { diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/Command.java b/app/src/main/java/ohi/andre/consolelauncher/commands/Command.java index e104cc4..340dbff 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/Command.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/Command.java @@ -41,7 +41,7 @@ public class Command { param.onArgNotFound(info, indexNotFound); } - if(pCmd.supportDefaultParam()) { + if(pCmd.defaultParamReference() != null) { if(args.length > nArgs) { return param.onNotArgEnough(info, nArgs); } @@ -53,8 +53,11 @@ public class Command { } else if(indexNotFound != -1) { return cmd.onArgNotFound(info, indexNotFound); } - else if (nArgs < cmd.minArgs() || (mArgs == null && cmd.minArgs() > 0)) { - return cmd.onNotArgEnough(info, nArgs); + else { + int[] args = cmd.argType(); + if (nArgs < args.length || (mArgs == null && args.length > 0)) { + return cmd.onNotArgEnough(info, nArgs); + } } String output = cmd.exec(info); @@ -80,8 +83,7 @@ public class Command { try { return args[useParamArgs ? nArgs - 1 : nArgs]; } catch (ArrayIndexOutOfBoundsException e) { - nArgs -= 1; - return nextArg(); + return 0; } } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java index 1f4f9c6..4130ecd 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java @@ -27,8 +27,6 @@ public interface CommandAbstraction { String exec(ExecutePack pack) throws Exception; - int minArgs(); - int[] argType(); int priority(); diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java index 9bec0aa..68b2e27 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java @@ -16,11 +16,13 @@ import ohi.andre.consolelauncher.managers.AppsManager; import ohi.andre.consolelauncher.managers.ContactManager; import ohi.andre.consolelauncher.managers.FileManager; import ohi.andre.consolelauncher.managers.FileManager.DirInfo; +import ohi.andre.consolelauncher.managers.RssManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.music.MusicManager2; import ohi.andre.consolelauncher.managers.notifications.NotificationManager; import ohi.andre.consolelauncher.managers.xml.options.Apps; import ohi.andre.consolelauncher.managers.xml.options.Notifications; +import ohi.andre.consolelauncher.managers.xml.options.Rss; import ohi.andre.consolelauncher.tuils.SimpleMutableEntry; import ohi.andre.consolelauncher.tuils.Tuils; @@ -366,9 +368,12 @@ public class CommandTuils { } String param = input.substring(0, indexOfFirstSpace).trim(); - if(param.length() > 0 && !param.startsWith("-")) param = "-".concat(param); + if(!param.startsWith("-")) param = "-".concat(param); + + Tuils.log(param); SimpleMutableEntry<Boolean, Param> sm = cmd.getParam(pack, param); + Tuils.log(sm.getKey(), sm.getValue()); Param p = sm.getValue(); boolean df = sm.getKey(); @@ -429,6 +434,7 @@ public class CommandTuils { } Collections.addAll(xmlPrefsEntrys, Apps.values()); Collections.addAll(xmlPrefsEntrys, Notifications.values()); + Collections.addAll(xmlPrefsEntrys, Rss.values()); } String candidate = index == -1 ? input : input.substring(0,index); @@ -447,6 +453,7 @@ public class CommandTuils { xmlPrefsFiles.add(element.path); xmlPrefsFiles.add(AppsManager.PATH); xmlPrefsFiles.add(NotificationManager.PATH); + xmlPrefsFiles.add(RssManager.PATH); } for(String xs : xmlPrefsFiles) { diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandsPreferences.java b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandsPreferences.java index 6d5b429..ef73e1d 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandsPreferences.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandsPreferences.java @@ -1,7 +1,5 @@ package ohi.andre.consolelauncher.commands; -import android.util.SparseArray; - import java.util.HashMap; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; @@ -13,30 +11,19 @@ import ohi.andre.consolelauncher.managers.xml.options.Cmd; public class CommandsPreferences { - public static final int DEFAULT_PARAM = 10; - - private HashMap<String, Preference> preferenceHashMap; + private HashMap<String, String> preferenceHashMap; public CommandsPreferences() { preferenceHashMap = new HashMap<>(); -// search - Preference searchP = new Preference(); - searchP.add(XMLPrefsManager.get(Cmd.default_search), DEFAULT_PARAM); - preferenceHashMap.put("search", searchP); - } - - public Preference forCommand(String cmd) { - return preferenceHashMap.get(cmd); + for(XMLPrefsManager.XMLPrefsSave save : Cmd.values()) { + preferenceHashMap.put(save.label(), XMLPrefsManager.get(save)); + } } - public class Preference { - SparseArray<String> prefs = new SparseArray<>(); - public void add(String pref, int id) { - prefs.put(id, pref); - } - public String get(int id) { - return prefs.get(id); - } + public String get(XMLPrefsManager.XMLPrefsSave save) { + String v = preferenceHashMap.get(save.label()); + if(v == null || v.length() == 0) v = save.defaultValue(); + return v; } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java index e247032..31881a6 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java @@ -25,6 +25,7 @@ import ohi.andre.consolelauncher.tuils.interfaces.Redirectator; import ohi.andre.consolelauncher.tuils.interfaces.Reloadable; import ohi.andre.consolelauncher.tuils.interfaces.Rooter; import ohi.andre.consolelauncher.tuils.libsuperuser.ShellHolder; +import okhttp3.OkHttpClient; /** * Created by francescoandreuzzi on 24/01/2017. @@ -81,8 +82,10 @@ public class MainPack extends ExecutePack { public RssManager rssManager; + public OkHttpClient client; + public MainPack(Context context, CommandGroup commandGroup, AliasManager alMgr, AppsManager appmgr, MusicManager2 p, - ContactManager c, Reloadable r, CommandExecuter executeCommand, Redirectator redirectator, ShellHolder shellHolder, RssManager rssManager) { + ContactManager c, Reloadable r, CommandExecuter executeCommand, Redirectator redirectator, ShellHolder shellHolder, RssManager rssManager, OkHttpClient client) { super(commandGroup); this.currentDirectory = XMLPrefsManager.get(File.class, Behavior.home_path); @@ -91,6 +94,8 @@ public class MainPack extends ExecutePack { this.rssManager = rssManager; + this.client = client; + this.res = context.getResources(); this.executer = executeCommand; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/airplane.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/airplane.java index aed3878..56cd940 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/airplane.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/airplane.java @@ -40,11 +40,6 @@ public class airplane extends APICommand { return 2; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java index f7600e9..03d6fe7 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java @@ -25,22 +25,32 @@ public class apps extends ParamCommand { ls { @Override public int[] args() { - return new int[0]; + return new int[] {CommandAbstraction.PLAIN_TEXT}; } @Override public String exec(ExecutePack pack) { + return ((MainPack) pack).appsManager.printApps(AppsManager.SHOWN_APPS, pack.getString()); + } + + @Override + public String onNotArgEnough(ExecutePack pack, int n) { return ((MainPack) pack).appsManager.printApps(AppsManager.SHOWN_APPS); } }, lsh { @Override public int[] args() { - return new int[0]; + return new int[] {CommandAbstraction.PLAIN_TEXT}; } @Override public String exec(ExecutePack pack) { + return ((MainPack) pack).appsManager.printApps(AppsManager.HIDDEN_APPS, pack.getString()); + } + + @Override + public String onNotArgEnough(ExecutePack pack, int n) { return ((MainPack) pack).appsManager.printApps(AppsManager.HIDDEN_APPS); } }, diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/beep.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/beep.java index b3b7f0a..81bd802 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/beep.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/beep.java @@ -26,11 +26,6 @@ public class beep implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/bluetooth.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/bluetooth.java index dbf60b7..073f0ce 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/bluetooth.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/bluetooth.java @@ -28,11 +28,6 @@ public class bluetooth implements CommandAbstraction { return R.string.help_bluetooth; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/calc.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/calc.java index 12b4875..e79a936 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/calc.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/calc.java @@ -22,11 +22,6 @@ public class calc extends PermanentSuggestionCommand { } } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[] {CommandAbstraction.PLAIN_TEXT}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/call.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/call.java index dd89f05..a82ac18 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/call.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/call.java @@ -51,11 +51,6 @@ public class call implements CommandAbstraction { return R.string.help_call; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[]{CommandAbstraction.CONTACTNUMBER}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/clear.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/clear.java index ec09b4f..e5c9790 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/clear.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/clear.java @@ -16,11 +16,6 @@ public class clear implements CommandAbstraction { return CLEAR; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/config.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/config.java index 3e8fbd6..7e62b13 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/config.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/config.java @@ -10,8 +10,9 @@ import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.specific.ParamCommand; import ohi.andre.consolelauncher.managers.AppsManager; -import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; +import ohi.andre.consolelauncher.managers.RssManager; import ohi.andre.consolelauncher.managers.notifications.NotificationManager; +import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Apps; import ohi.andre.consolelauncher.managers.xml.options.Notifications; import ohi.andre.consolelauncher.tuils.Tuils; @@ -47,6 +48,21 @@ public class config extends ParamCommand { return set.exec(pack); } }, + info { + @Override + public int[] args() { + return new int[] {CommandAbstraction.CONFIG_ENTRY}; + } + + @Override + public String exec(ExecutePack pack) { + XMLPrefsManager.XMLPrefsSave save = pack.getPrefsSave(); + + return "Type:" + Tuils.SPACE + save.type() + Tuils.NEWLINE + + "Default:" + Tuils.SPACE + save.defaultValue() + Tuils.NEWLINE + + save.info(); + } + }, file { @Override public int[] args() { @@ -119,6 +135,27 @@ public class config extends ParamCommand { } } + if(name.equalsIgnoreCase(AppsManager.PATH)) { + List<String> strings = AppsManager.instance.getValues().values(); + Tuils.addPrefix(strings, Tuils.DOUBLE_SPACE); + strings.add(0, AppsManager.PATH); + return Tuils.toPlanString(strings, Tuils.NEWLINE); + } + + if(name.equalsIgnoreCase(NotificationManager.PATH)) { + List<String> strings = NotificationManager.instance.getValues().values(); + Tuils.addPrefix(strings, Tuils.DOUBLE_SPACE); + strings.add(0, NotificationManager.PATH); + return Tuils.toPlanString(strings, Tuils.NEWLINE); + } + + if(name.equalsIgnoreCase(RssManager.PATH)) { + List<String> strings = NotificationManager.instance.getValues().values(); + Tuils.addPrefix(strings, Tuils.DOUBLE_SPACE); + strings.add(0, RssManager.PATH); + return Tuils.toPlanString(strings, Tuils.NEWLINE); + } + return "[]"; } diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java index ecff729..ea26d99 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java @@ -5,6 +5,7 @@ import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.commands.CommandAbstraction; import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; +import ohi.andre.consolelauncher.tuils.StoppableThread; /** * Created by francescoandreuzzi on 26/07/2017. @@ -14,7 +15,7 @@ public class ctrlc implements CommandAbstraction { @Override public String exec(final ExecutePack pack) throws Exception { - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); @@ -32,11 +33,6 @@ public class ctrlc implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java index c1263a2..52fbc65 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java @@ -69,11 +69,6 @@ public class data extends APICommand { return R.string.help_data; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/donate.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/donate.java index 4af9b04..abfc603 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/donate.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/donate.java @@ -34,11 +34,6 @@ public class donate implements CommandAbstraction { return info.res.getString(R.string.output_rate); } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/exit.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/exit.java index 889db6a..73f0f94 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/exit.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/exit.java @@ -16,11 +16,6 @@ public class exit implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/flash.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/flash.java index 6e1d05f..e6a31fd 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/flash.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/flash.java @@ -129,11 +129,6 @@ public class flash implements CommandAbstraction { return R.string.help_flash; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java index 5644336..aac066c 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java @@ -27,11 +27,6 @@ public class help implements CommandAbstraction { return R.string.help_help; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[]{CommandAbstraction.COMMAND}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/location.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/location.java index 592c1f7..a848ec4 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/location.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/location.java @@ -73,11 +73,6 @@ public class location extends APICommand { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/notifications.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/notifications.java index 683132a..8823f80 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/notifications.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/notifications.java @@ -23,8 +23,9 @@ public class notifications extends ParamCommand { inc { @Override public String exec(ExecutePack pack) { - NotificationManager.notificationsChangeFor(new NotificationManager.NotificatedApp(pack.getLaunchInfo().componentName.getPackageName(), null, true)); - return null; + String output = NotificationManager.setState(pack.getLaunchInfo().componentName.getPackageName(), true); + if(output == null || output.length() == 0) return null; + return output; } @Override @@ -40,8 +41,9 @@ public class notifications extends ParamCommand { exc { @Override public String exec(ExecutePack pack) { - NotificationManager.notificationsChangeFor(new NotificationManager.NotificatedApp(pack.getLaunchInfo().componentName.getPackageName(), null, false)); - return null; + String output = NotificationManager.setState(pack.getLaunchInfo().componentName.getPackageName(), false); + if(output == null || output.length() == 0) return null; + return output; } @Override @@ -54,15 +56,13 @@ public class notifications extends ParamCommand { return pack.context.getString(R.string.output_appnotfound); } }, - clr { + color { @Override public String exec(ExecutePack pack) { - try { - String s = pack.getString(); - - NotificationManager.notificationsChangeFor(new NotificationManager.NotificatedApp(pack.getLaunchInfo().componentName.getPackageName(), s, true)); - } catch (Exception e) {} - return null; + String color = pack.getString(); + String output = NotificationManager.setColor(pack.getLaunchInfo().componentName.getPackageName(), color); + if(output == null || output.length() == 0) return null; + return output; } @Override @@ -79,12 +79,32 @@ public class notifications extends ParamCommand { return pack.context.getString(res); } }, - title_filter { + format { + @Override + public String exec(ExecutePack pack) { + String s = pack.getString(); + String output = NotificationManager.setFormat(pack.getLaunchInfo().componentName.getPackageName(), s); + if(output == null || output.length() == 0) return null; + return output; + } + + @Override + public int[] args() { + return new int[] {CommandAbstraction.NO_SPACE_STRING, CommandAbstraction.VISIBLE_PACKAGE}; + } + + @Override + public String onArgNotFound(ExecutePack pack, int index) { + return pack.context.getString(R.string.invalid_integer); + } + }, + add_filter { @Override public String exec(ExecutePack pack) { int id = pack.getInt(); - NotificationManager.excludeRegex(pack.getString(), "title", id); - return null; + String output = NotificationManager.addFilter(pack.getString(), id); + if(output == null || output.length() == 0) return null; + return output; } @Override @@ -97,12 +117,13 @@ public class notifications extends ParamCommand { return pack.context.getString(R.string.invalid_integer); } }, - text_filter { + add_format { @Override public String exec(ExecutePack pack) { int id = pack.getInt(); - NotificationManager.excludeRegex(pack.getString(), "text", id); - return null; + String output = NotificationManager.addFormat(pack.getString(), id); + if(output == null || output.length() == 0) return null; + return output; } @Override @@ -115,26 +136,40 @@ public class notifications extends ParamCommand { return pack.context.getString(R.string.invalid_integer); } }, - apply_filter { + rm_filter { + @Override + public String exec(ExecutePack pack) { + String output = NotificationManager.rmFilter(pack.getInt()); + if(output == null || output.length() == 0) return null; + return output; + } + @Override public int[] args() { - return new int[] {CommandAbstraction.INT, CommandAbstraction.VISIBLE_PACKAGE}; + return new int[] {CommandAbstraction.INT}; } + @Override + public String onArgNotFound(ExecutePack pack, int index) { + return pack.context.getString(R.string.invalid_integer); + } + }, + rm_format { @Override public String exec(ExecutePack pack) { - int id = pack.getInt(); - NotificationManager.applyFilter(id, pack.getLaunchInfo().componentName.getPackageName()); - return null; + String output = NotificationManager.rmFormat(pack.getInt()); + if(output == null || output.length() == 0) return null; + return output; } @Override - public String onArgNotFound(ExecutePack pack, int index) { - int res; - if(index == 1) res = R.string.invalid_integer; - else res = R.string.output_appnotfound; + public int[] args() { + return new int[] {CommandAbstraction.INT}; + } - return pack.context.getString(res); + @Override + public String onArgNotFound(ExecutePack pack, int index) { + return pack.context.getString(R.string.invalid_integer); } }, file { diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/open.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/open.java index 1b330d1..d5a5d4c 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/open.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/open.java @@ -29,11 +29,6 @@ public class open implements CommandAbstraction { return R.string.help_open; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[]{CommandAbstraction.FILE}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rate.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rate.java index a04db5a..3141867 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rate.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rate.java @@ -13,26 +13,13 @@ public class rate implements CommandAbstraction { @Override public String exec(ExecutePack pack) { final MainPack info = (MainPack) pack; - new Thread() { - @Override - public void run() { - super.run(); - - try { - sleep(300); - } catch (InterruptedException e) { - } - - try { - info.context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + - info.context.getPackageName()))); - } catch (android.content.ActivityNotFoundException anfe) { - info.context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + - info.context.getPackageName()))); - } - } - }.start(); - + try { + info.context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + + info.context.getPackageName()))); + } catch (android.content.ActivityNotFoundException anfe) { + info.context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + + info.context.getPackageName()))); + } return info.res.getString(R.string.output_rate); } @@ -42,11 +29,6 @@ public class rate implements CommandAbstraction { return R.string.help_rate; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/refresh.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/refresh.java index da18834..10be64a 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/refresh.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/refresh.java @@ -24,11 +24,6 @@ public class refresh implements CommandAbstraction { return R.string.help_refresh; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/regex.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/regex.java index 1440638..b14de39 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/regex.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/regex.java @@ -21,7 +21,7 @@ public class regex extends ParamCommand { public String exec(ExecutePack pack) { String output = RegexManager.add(pack.getInt(), pack.getString()); if(output == null) return null; - if(output.length() == 0) return pack.context.getString(R.string.regex_exists); + if(output.length() == 0) return pack.context.getString(R.string.id_already); else return output; } @@ -35,7 +35,7 @@ public class regex extends ParamCommand { public String exec(ExecutePack pack) { String output = RegexManager.rm(pack.getInt()); if(output == null) return null; - if(output.length() == 0) return pack.context.getString(R.string.regex_not_found); + if(output.length() == 0) return pack.context.getString(R.string.id_notfound); return output; } @@ -50,7 +50,7 @@ public class regex extends ParamCommand { RegexManager.Regex r = RegexManager.get(pack.getInt()); if(r == null) return pack.context.getString(R.string.id_notfound); - return r.value; + return r.regex != null ? r.regex.pattern() : r.literalPattern; } @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/restart.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/restart.java index 4a4016c..b88c5b9 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/restart.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/restart.java @@ -14,11 +14,6 @@ public class restart implements CommandAbstraction { return pack.context.getString(R.string.restarting); } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rss.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rss.java index dadca04..67e88a8 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rss.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rss.java @@ -1,8 +1,10 @@ package ohi.andre.consolelauncher.commands.main.raw; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + import java.io.File; -import java.text.DateFormat; -import java.text.SimpleDateFormat; +import java.util.regex.Pattern; import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.commands.CommandAbstraction; @@ -10,9 +12,9 @@ import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.specific.ParamCommand; import ohi.andre.consolelauncher.managers.RssManager; +import ohi.andre.consolelauncher.managers.TimeManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Rss; -import ohi.andre.consolelauncher.tuils.TimeManager; import ohi.andre.consolelauncher.tuils.Tuils; /** @@ -68,8 +70,7 @@ public class rss extends ParamCommand { @Override public String exec(ExecutePack pack) { int id = pack.getInt(); - ((MainPack) pack).rssManager.l(id); - return null; + return ((MainPack) pack).rssManager.l(id); } @Override @@ -91,7 +92,7 @@ public class rss extends ParamCommand { return new int[] {CommandAbstraction.INT, CommandAbstraction.BOOLEAN}; } }, - time { + update_time { @Override public String exec(ExecutePack pack) { int id = pack.getInt(); @@ -105,6 +106,17 @@ public class rss extends ParamCommand { return new int[] {CommandAbstraction.INT, CommandAbstraction.LONG}; } }, + time_format { + @Override + public String exec(ExecutePack pack) { + return ((MainPack) pack).rssManager.setTimeFormat(pack.getInt(), pack.getString()); + } + + @Override + public int[] args() { + return new int[] {CommandAbstraction.INT, CommandAbstraction.PLAIN_TEXT}; + } + }, format { @Override public int[] args() { @@ -139,6 +151,28 @@ public class rss extends ParamCommand { return super.onArgNotFound(pack, index); } }, + entry_tag { + @Override + public int[] args() { + return new int[] {CommandAbstraction.INT, CommandAbstraction.PLAIN_TEXT}; + } + + @Override + public String exec(ExecutePack pack) { + return ((MainPack) pack).rssManager.setEntryTag(pack.getInt(), pack.getString()); + } + }, + date_tag { + @Override + public int[] args() { + return new int[] {CommandAbstraction.INT, CommandAbstraction.PLAIN_TEXT}; + } + + @Override + public String exec(ExecutePack pack) { + return ((MainPack) pack).rssManager.setDateTag(pack.getInt(), pack.getString()); + } + }, last_check { @Override public int[] args() { @@ -147,16 +181,19 @@ public class rss extends ParamCommand { @Override public String exec(ExecutePack pack) { - String output = XMLPrefsManager.attrValue(new File(Tuils.getFolder(), RssManager.PATH), RssManager.NAME, - RssManager.RSS_LABEL, new String[] {RssManager.ID_ATTRIBUTE}, new String[] {String.valueOf(pack.getInt())}, - RssManager.LASTCHECKED_ATTRIBUTE); - if(output == null || output.length() == 0) return pack.context.getString(R.string.id_notfound); + Node n = XMLPrefsManager.findNode(new File(Tuils.getFolder(), RssManager.PATH), RssManager.RSS_LABEL, new String[] {RssManager.ID_ATTRIBUTE}, new String[] {String.valueOf(pack.getInt())}); + if(n == null) return pack.context.getString(R.string.id_notfound); + + Element el = (Element) n; + + String value = el.hasAttribute(RssManager.LASTCHECKED_ATTRIBUTE) ? el.getAttribute(RssManager.LASTCHECKED_ATTRIBUTE) : null; + if(value == null) return pack.context.getString(R.string.rss_never_checked); try { - DateFormat defaultRSSDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z"); - return TimeManager.replace(XMLPrefsManager.get(Rss.rss_time_format), defaultRSSDateFormat.parse(output).getTime(), + return TimeManager.replace(XMLPrefsManager.get(Rss.rss_time_format), Long.parseLong(value), Integer.MAX_VALUE).toString(); } catch (Exception e) { + Tuils.log(e); return pack.context.getString(R.string.output_error); } } @@ -169,6 +206,7 @@ public class rss extends ParamCommand { @Override public String exec(ExecutePack pack) { + if(!((MainPack) pack).rssManager.updateRss(pack.getInt(), false, true)) return pack.context.getString(R.string.id_notfound); return null; } }, @@ -180,7 +218,10 @@ public class rss extends ParamCommand { @Override public String exec(ExecutePack pack) { - return null; + RssManager.Rss rss = ((MainPack) pack).rssManager.findId(pack.getInt()); + if(rss == null) return pack.context.getString(R.string.id_notfound); + + return rss.toString(); } }, include_if_matches { @@ -211,6 +252,40 @@ public class rss extends ParamCommand { return ((MainPack) pack).rssManager.setExcludeIfMatches(id, r); } }, + add_command { + @Override + public int[] args() { + return new int[] {CommandAbstraction.INT, CommandAbstraction.NO_SPACE_STRING, CommandAbstraction.NO_SPACE_STRING, CommandAbstraction.PLAIN_TEXT}; + } + + @Override + public String exec(ExecutePack pack) { + int id = pack.getInt(); + + String on = pack.getString(); + String regex = pack.getString(); + String cmd = pack.getString(); + + try { + Pattern.compile(regex); + } catch (Exception e) { + return e.toString(); + } + + return ((MainPack) pack).rssManager.addRegexCommand(id, on, regex, cmd); + } + }, + rm_command { + @Override + public int[] args() { + return new int[] {CommandAbstraction.INT}; + } + + @Override + public String exec(ExecutePack pack) { + return ((MainPack) pack).rssManager.rmRegexCommand(pack.getInt()); + } + }, wifi_only { @Override public int[] args() { diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java index d695816..6cef058 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java @@ -11,6 +11,8 @@ import ohi.andre.consolelauncher.commands.CommandAbstraction; import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.specific.ParamCommand; +import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; +import ohi.andre.consolelauncher.managers.xml.options.Cmd; import ohi.andre.consolelauncher.tuils.Tuils; public class search extends ParamCommand { @@ -114,8 +116,8 @@ public class search extends ParamCommand { } @Override - public boolean supportDefaultParam() { - return true; + public XMLPrefsManager.XMLPrefsSave defaultParamReference() { + return Cmd.default_search; } @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/share.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/share.java index a80432e..d2e4656 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/share.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/share.java @@ -30,11 +30,6 @@ public class share implements CommandAbstraction { return R.string.help_share; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[]{CommandAbstraction.FILE}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java index 70fae98..04d23be 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java @@ -57,11 +57,6 @@ public class shellcommands implements CommandAbstraction { return commands; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/sms.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/sms.java index 2f8fe26..4ff27ae 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/sms.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/sms.java @@ -40,11 +40,6 @@ public class sms extends RedirectCommand { return null; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[] {CommandAbstraction.CONTACTNUMBER}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/status.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/status.java index 613167b..33c7cd6 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/status.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/status.java @@ -55,11 +55,6 @@ public class status implements CommandAbstraction { info.res.getString(R.string.mobile_data) + Tuils.SPACE + mobileOn; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/time.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/time.java index 8d94335..01620f8 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/time.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/time.java @@ -3,7 +3,7 @@ package ohi.andre.consolelauncher.commands.main.raw; import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.commands.CommandAbstraction; import ohi.andre.consolelauncher.commands.ExecutePack; -import ohi.andre.consolelauncher.tuils.TimeManager; +import ohi.andre.consolelauncher.managers.TimeManager; /** * Created by andre on 03/12/15. @@ -15,11 +15,6 @@ public class time implements CommandAbstraction { return TimeManager.replace("%t" + index).toString(); } - @Override - public int minArgs() { - return 1; - } - @Override public int priority() { return 4; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tuixt.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tuixt.java index fc3eb02..af4addf 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tuixt.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tuixt.java @@ -37,11 +37,6 @@ public class tuixt implements CommandAbstraction { return Tuils.EMPTYSTRING; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[] {CommandAbstraction.FILE}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tutorial.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tutorial.java index 1cab1a6..09c01f1 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tutorial.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tutorial.java @@ -22,11 +22,6 @@ public class tutorial implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java index e1c9543..a4f2155 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java @@ -29,11 +29,6 @@ public class uninstall implements CommandAbstraction { return R.string.help_uninstall; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[]{CommandAbstraction.VISIBLE_PACKAGE}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/vibrate.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/vibrate.java index a3c6e51..f98c6ba 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/vibrate.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/vibrate.java @@ -60,11 +60,6 @@ public class vibrate implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[] {CommandAbstraction.PLAIN_TEXT}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/wifi.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/wifi.java index d82ff97..93dafb9 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/wifi.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/wifi.java @@ -25,11 +25,6 @@ public class wifi implements CommandAbstraction { return R.string.help_wifi; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/specific/ParamCommand.java b/app/src/main/java/ohi/andre/consolelauncher/commands/specific/ParamCommand.java index ef77f6e..606a498 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/specific/ParamCommand.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/specific/ParamCommand.java @@ -2,10 +2,10 @@ package ohi.andre.consolelauncher.commands.specific; import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.commands.CommandAbstraction; -import ohi.andre.consolelauncher.commands.CommandsPreferences; import ohi.andre.consolelauncher.commands.ExecutePack; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.main.Param; +import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.tuils.SimpleMutableEntry; import ohi.andre.consolelauncher.tuils.Tuils; @@ -20,11 +20,6 @@ public abstract class ParamCommand implements CommandAbstraction { return new int[] {CommandAbstraction.PARAM}; } - @Override - public int minArgs() { - return -1; - } - @Override public final String exec(ExecutePack pack) throws Exception { String o = doThings(pack); @@ -32,21 +27,22 @@ public abstract class ParamCommand implements CommandAbstraction { Param param = pack.get(Param.class); if(param == null) { - return pack.context.getString(R.string.output_invalid_param) + Tuils.SPACE + pack.get(String.class, 0); + Object o1 = pack.get(Object.class, 0); + return pack.context.getString(R.string.output_invalid_param) + Tuils.SPACE + (o1 != null ? o1.toString() : "null"); } return param.exec(pack); } public SimpleMutableEntry<Boolean, Param> getParam(MainPack pack, String param) { Param p = paramForString(pack, param); - if(p == null && supportDefaultParam()) { + if(p == null && defaultParamReference() != null) { return new SimpleMutableEntry<>(true, paramForString(pack, defaultParam(pack))); } return new SimpleMutableEntry<>(false, p); } public String defaultParam(MainPack pack) { - String def = pack.cmdPrefs.forCommand(getClass().getSimpleName()).get(CommandsPreferences.DEFAULT_PARAM); + String def = pack.cmdPrefs.get(defaultParamReference()); if(!def.startsWith("-")) def = "-" + def; return def; } @@ -71,7 +67,7 @@ public abstract class ParamCommand implements CommandAbstraction { public abstract String[] params(); protected abstract Param paramForString(MainPack pack, String param); protected abstract String doThings(ExecutePack pack); - public boolean supportDefaultParam() { - return false; + public XMLPrefsManager.XMLPrefsSave defaultParamReference() { + return null; } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/TuixtActivity.java b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/TuixtActivity.java index 092c5f1..4fe6aa3 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/TuixtActivity.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/TuixtActivity.java @@ -34,6 +34,7 @@ import ohi.andre.consolelauncher.commands.CommandTuils; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.managers.xml.options.Ui; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; /** @@ -199,7 +200,7 @@ public class TuixtActivity extends Activity { pack = new TuixtPack(group, file, this, fileView); fileView.setText(getString(R.string.tuixt_reading) + Tuils.SPACE + path); - new Thread() { + new StoppableThread() { @Override public void run() { diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/exit.java b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/exit.java index b6fb29f..27f43a8 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/exit.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/exit.java @@ -21,11 +21,6 @@ public class exit implements CommandAbstraction { return null; } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java index 57a6ae5..4bb8db2 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java @@ -27,11 +27,6 @@ public class help implements CommandAbstraction { return pack.resources.getString(res); } - @Override - public int minArgs() { - return 1; - } - @Override public int[] argType() { return new int[] {CommandAbstraction.COMMAND}; diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/save.java b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/save.java index cb891f1..e7a9307 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/save.java +++ b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/save.java @@ -26,11 +26,6 @@ public class save implements CommandAbstraction { } } - @Override - public int minArgs() { - return 0; - } - @Override public int[] argType() { return new int[0]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/AliasManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/AliasManager.java index a16e414..5314d8f 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/AliasManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/AliasManager.java @@ -11,11 +11,10 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.InputStreamReader; +import java.util.AbstractMap; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -29,7 +28,7 @@ public class AliasManager implements Reloadable { public static final String PATH = "alias.txt"; - private Map<String, String> aliases; + private List<Map.Entry<String, String>> aliases; private String paramMarker, paramSeparator, aliasLabelFormat; private Context context; @@ -46,7 +45,7 @@ public class AliasManager implements Reloadable { public String printAliases() { String output = Tuils.EMPTYSTRING; - for (Map.Entry<String, String> entry : aliases.entrySet()) { + for (Map.Entry<String, String> entry : aliases) { output = output.concat(entry.getKey() + " --> " + entry.getValue() + Tuils.NEWLINE); } @@ -82,7 +81,7 @@ public class AliasManager implements Reloadable { String aliasValue = null; while (true) { - aliasValue = aliases.get(alias); + aliasValue = getALiasFor(alias); if(aliasValue != null) break; else { int index = alias.lastIndexOf(Tuils.SPACE); @@ -96,7 +95,7 @@ public class AliasManager implements Reloadable { return new String[] {aliasValue, alias, args}; } else { - return new String[] {aliases.get(alias), alias, Tuils.EMPTYSTRING}; + return new String[] {getALiasFor(alias), alias, Tuils.EMPTYSTRING}; } } @@ -113,6 +112,24 @@ public class AliasManager implements Reloadable { return aliasValue; } + private String getALiasFor(String name) { + for(Map.Entry<String, String> entry : aliases) { + if(name.equals(entry.getKey())) return entry.getValue(); + } + + return null; + } + + private void removeAliasFor(String name) { + for(int c = 0; c < aliases.size(); c++) { + Map.Entry e = aliases.get(c); + if(name.equals(e.getKey())) { + aliases.remove(c); + return; + } + } + } + private final Pattern pn = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); private final Pattern pv = Pattern.compile("%v", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); private final Pattern pa = Pattern.compile("%a", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); @@ -127,7 +144,7 @@ public class AliasManager implements Reloadable { @Override public void reload() { if(aliases != null) aliases.clear(); - else aliases = new HashMap(); + else aliases = new ArrayList<>(); File file = new File(Tuils.getFolder(), PATH); @@ -159,7 +176,7 @@ public class AliasManager implements Reloadable { Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_notaddingalias1) + Tuils.SPACE + name + Tuils.SPACE + context.getString(R.string.output_notaddingalias3)); } else { - aliases.put(name, value); + aliases.add(new AbstractMap.SimpleImmutableEntry<>(name, value)); } } } catch (Exception e) {} @@ -173,7 +190,8 @@ public class AliasManager implements Reloadable { fos.write((Tuils.NEWLINE + name + "=" + value).getBytes()); fos.close(); - aliases.put(name, value); + aliases.add(new AbstractMap.SimpleImmutableEntry<>(name, value)); + return true; } catch (Exception e) { return false; @@ -201,7 +219,7 @@ public class AliasManager implements Reloadable { reader.close(); - aliases.remove(name); + removeAliasFor(name); return tempFile.renameTo(inputFile); } catch (Exception e) { @@ -213,8 +231,7 @@ public class AliasManager implements Reloadable { List<String> aliasKeys = new ArrayList<>(0); if(aliases == null) return aliasKeys; - Set<String> keys = aliases.keySet(); - for(String s : keys) aliasKeys.add(s); + for(Map.Entry<String, String> entry : aliases) aliasKeys.add(entry.getKey()); return aliasKeys; } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java index b6bdbe7..63c0d41 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -38,7 +39,7 @@ import ohi.andre.consolelauncher.managers.xml.options.Behavior; import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.managers.xml.options.Ui; import ohi.andre.consolelauncher.tuils.Compare; -import ohi.andre.consolelauncher.tuils.TimeManager; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; import ohi.andre.consolelauncher.tuils.interfaces.Suggester; @@ -88,7 +89,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { @Override public void write(XMLPrefsManager.XMLPrefsSave save, String value) { - set(new File(Tuils.getFolder(), PATH), NAME, save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); + set(new File(Tuils.getFolder(), PATH), save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); } @Override @@ -140,7 +141,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { initAppListener(context); - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); @@ -176,9 +177,12 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { Object[] o; try { o = XMLPrefsManager.buildDocument(file, NAME); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return; + } } catch (Exception e) { - Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + PATH + context.getString(R.string.output_xmlproblem2) + - Tuils.NEWLINE + context.getString(R.string.output_errorlabel) + e.toString()); + Tuils.sendXMLParseError(context, PATH, e); return; } @@ -215,7 +219,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { continue; } - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); @@ -434,6 +438,8 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } public LaunchInfo findLaunchInfoWithLabel(String label, int type) { + if(appsHolder == null) return null; + List<LaunchInfo> appList; if(type == SHOWN_APPS) { appList = appsHolder.getApps(); @@ -441,6 +447,8 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { appList = hiddenApps; } + if(appList == null) return null; + LaunchInfo i = AppUtils.findLaunchInfoWithLabel(appList, label); if(i != null) return i; @@ -460,11 +468,17 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { if(appsHolder != null) appsHolder.update(true); } - public Intent getIntent(LaunchInfo info) { + public Intent getIntent(final LaunchInfo info) { info.launchedTimes++; - appsHolder.requestSuggestionUpdate(info); + new StoppableThread() { + @Override + public void run() { + super.run(); - writeLaunchTimes(info); + appsHolder.requestSuggestionUpdate(info); + writeLaunchTimes(info); + } + }.start(); Intent intent = new Intent(Intent.ACTION_MAIN); intent.setComponent(info.componentName); @@ -474,7 +488,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } public String hideActivity(LaunchInfo info) { - set(file, NAME, info.write(), new String[] {SHOW_ATTRIBUTE}, new String[] {false + Tuils.EMPTYSTRING}); + set(file, info.write(), new String[] {SHOW_ATTRIBUTE}, new String[] {false + Tuils.EMPTYSTRING}); appsHolder.remove(info); appsHolder.update(true); @@ -485,7 +499,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } public String showActivity(LaunchInfo info) { - set(file, NAME, info.write(), new String[]{SHOW_ATTRIBUTE}, new String[]{true + Tuils.EMPTYSTRING}); + set(file, info.write(), new String[]{SHOW_ATTRIBUTE}, new String[]{true + Tuils.EMPTYSTRING}); hiddenApps.remove(info); appsHolder.add(info); @@ -498,7 +512,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { int index = Tuils.find(name, groups); if(index == -1) { groups.add(new Group(name)); - return XMLPrefsManager.set(file, NAME, name, new String[]{APPS_ATTRIBUTE}, new String[]{Tuils.EMPTYSTRING}); + return XMLPrefsManager.set(file, name, new String[]{APPS_ATTRIBUTE}, new String[]{Tuils.EMPTYSTRING}); } return context.getString(R.string.output_groupexists); @@ -511,7 +525,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } groups.get(index).setBgColor(Color.parseColor(color)); - return XMLPrefsManager.set(file, NAME, name, new String[]{BGCOLOR_ATTRIBUTE}, new String[]{color}); + return XMLPrefsManager.set(file, name, new String[]{BGCOLOR_ATTRIBUTE}, new String[]{color}); } public String groupForeColor(String name, String color) { @@ -521,11 +535,11 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } groups.get(index).setForeColor(Color.parseColor(color)); - return XMLPrefsManager.set(file, NAME, name, new String[]{FORECOLOR_ATTRIBUTE}, new String[]{color}); + return XMLPrefsManager.set(file, name, new String[]{FORECOLOR_ATTRIBUTE}, new String[]{color}); } public String removeGroup(String name) { - String output = XMLPrefsManager.removeNode(file, NAME, name); + String output = XMLPrefsManager.removeNode(file, name); if(output == null) return null; if(output.length() == 0) return context.getString(R.string.output_groupnotfound); @@ -539,7 +553,11 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { public String addAppToGroup(String group, LaunchInfo app) { Object[] o; try { - o = XMLPrefsManager.buildDocument(file, NAME); + o = XMLPrefsManager.buildDocument(file, null); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return null; + } } catch (Exception e) { return e.toString(); } @@ -571,7 +589,11 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { public String removeAppFromGroup(String group, LaunchInfo app) { Object[] o; try { - o = XMLPrefsManager.buildDocument(file, NAME); + o = XMLPrefsManager.buildDocument(file, null); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return null; + } } catch (Exception e) { return e.toString(); } @@ -653,7 +675,11 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { public String listGroup(String group) { Object[] o; try { - o = XMLPrefsManager.buildDocument(file, NAME); + o = XMLPrefsManager.buildDocument(file, null); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return null; + } } catch (Exception e) { return e.toString(); } @@ -701,7 +727,11 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { public String listGroups() { Object[] o; try { - o = XMLPrefsManager.buildDocument(file, NAME); + o = XMLPrefsManager.buildDocument(file, null); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return null; + } } catch (Exception e) { return e.toString(); } @@ -740,8 +770,58 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { } public String printApps(int type) { + return printNApps(type, -1); + } + + public String printApps(int type, String text) { + boolean ok; + int length = 0; + try { + length = Integer.parseInt(text); + ok = true; + } catch (NumberFormatException exc) { + ok = false; + } + + if(ok) { + return printNApps(type, length); + } else { + return printAppsThatBegins(type, text); + } + } + + private String printNApps(int type, int n) { + try { + List<String> labels = AppUtils.labelList(type == SHOWN_APPS ? appsHolder.getApps() : hiddenApps, true); + + if(n >= 0) { + int toRemove = labels.size() - n; + if(toRemove <= 0) return "[]"; + + for(int c = 0; c < toRemove; c++) { + labels.remove(labels.size() - 1); + } + } + + return AppUtils.printApps(labels); + } catch (NullPointerException e) { + return "[]"; + } + } + + private String printAppsThatBegins(int type, String with) { try { List<String> labels = AppUtils.labelList(type == SHOWN_APPS ? appsHolder.getApps() : hiddenApps, true); + + if(with != null && with.length() > 0) { + with = with.toLowerCase(); + + Iterator<String> it = labels.iterator(); + while(it.hasNext()) { + if(!it.next().toLowerCase().startsWith(with)) it.remove(); + } + } + return AppUtils.printApps(labels); } catch (NullPointerException e) { return "[]"; @@ -853,12 +933,13 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { public ComponentName componentName; - public String publicLabel; + public String publicLabel, unspacedLowercaseLabel; public int launchedTimes = 0; public LaunchInfo(String packageName, String activityName, String label) { this.componentName = new ComponentName(packageName, activityName); this.publicLabel = label; + this.unspacedLowercaseLabel = Tuils.removeSpaces(this.publicLabel).toLowerCase(); } public boolean isInside(String apps) { @@ -1196,17 +1277,15 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement { return null; } - private static Pattern removeSpacePattern = Pattern.compile("\\s+"); public static LaunchInfo findLaunchInfoWithLabel(List<LaunchInfo> appList, String label) { - label = removeSpacePattern.matcher(label).replaceAll(Tuils.EMPTYSTRING); - for(LaunchInfo i : appList) if(removeSpacePattern.matcher(i.publicLabel).replaceAll(Tuils.EMPTYSTRING).equalsIgnoreCase(label)) return i; + label = Tuils.removeSpaces(label); + for(LaunchInfo i : appList) if(i.unspacedLowercaseLabel.equalsIgnoreCase(label)) return i; return null; } private static List<LaunchInfo> findLaunchInfosWithPackage(String packageName, List<LaunchInfo> infos) { List<LaunchInfo> result = new ArrayList<>(); for(LaunchInfo info : infos) if (info.componentName.getPackageName().equals(packageName)) result.add(info); - return result; } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java index c92139d..ea9b64c 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java @@ -17,6 +17,7 @@ import java.util.List; import ohi.andre.consolelauncher.LauncherActivity; import ohi.andre.consolelauncher.tuils.Compare; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; public class ContactManager { @@ -38,7 +39,7 @@ public class ContactManager { return; } - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/MessagesManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/MessagesManager.java index 7da2018..a4f03b7 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/MessagesManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/MessagesManager.java @@ -58,15 +58,15 @@ public class MessagesManager { return; } - Tuils.sendOutput(color, context, MARKER + Tuils.NEWLINE + copy.remove(index).msg + Tuils.NEWLINE + MARKER); + Tuils.sendOutput(color, context, MARKER + Tuils.NEWLINE + context.getString(copy.remove(index).msg) + Tuils.NEWLINE + MARKER); } } public static class Message { - String msg; + int msg; // more coming - public Message(String msg) { + public Message(int msg) { this.msg = msg; } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/RegexManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/RegexManager.java index 646ace6..dbb5277 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/RegexManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/RegexManager.java @@ -1,7 +1,6 @@ package ohi.andre.consolelauncher.managers; import android.content.Context; -import android.graphics.Color; import android.text.SpannableString; import org.w3c.dom.Document; @@ -15,9 +14,9 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Theme; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; /** @@ -34,57 +33,74 @@ public class RegexManager { private RegexManager() {} private static boolean available = false; - public static void create(Context context) { + public static void create(final Context context) { if(available) return; available = true; if(regexes != null) regexes.clear(); else regexes = new ArrayList<>(); - try { - File file = new File(Tuils.getFolder(), PATH); - if(!file.exists()) { - file.createNewFile(); - XMLPrefsManager.resetFile(file, ROOT); - } + new StoppableThread() { - Object[] o = XMLPrefsManager.buildDocument(file, ROOT); + @Override + public void run() { + super.run(); - Element el = (Element) o[1]; - - List<Integer> busyIds = new ArrayList<>(); - - NodeList nodeList = el.getElementsByTagName(REGEX_LABEL); - - Out: - for(int c = 0; c < nodeList.getLength(); c++) { - Element e = (Element) nodeList.item(c); - - if(!e.hasAttribute(XMLPrefsManager.VALUE_ATTRIBUTE)) continue; - String value = e.getAttribute(XMLPrefsManager.VALUE_ATTRIBUTE); - - int id; try { - id = Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)); - } catch (Exception exc) { - continue; - } - - for(int j = 0; j < busyIds.size(); j++) { - if((int) busyIds.get(j) == id) continue Out; - } - - busyIds.add(id); - - if(value != null && value.length() > 0) { - regexes.add(new Regex(value, id)); + File file = new File(Tuils.getFolder(), PATH); + if(!file.exists()) { + file.createNewFile(); + XMLPrefsManager.resetFile(file, ROOT); + } + + Object[] o; + try { + o = XMLPrefsManager.buildDocument(file, ROOT); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return; + } + } catch (Exception e) { + Tuils.sendXMLParseError(context, PATH, e); + return; + } + + Element el = (Element) o[1]; + + List<Integer> busyIds = new ArrayList<>(); + + NodeList nodeList = el.getElementsByTagName(REGEX_LABEL); + + Out: + for(int c = 0; c < nodeList.getLength(); c++) { + Element e = (Element) nodeList.item(c); + + if(!e.hasAttribute(XMLPrefsManager.VALUE_ATTRIBUTE)) continue; + String value = e.getAttribute(XMLPrefsManager.VALUE_ATTRIBUTE); + + int id; + try { + id = Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)); + } catch (Exception exc) { + continue; + } + + for(int j = 0; j < busyIds.size(); j++) { + if((int) busyIds.get(j) == id) continue Out; + } + + busyIds.add(id); + + if(value != null && value.length() > 0) { + regexes.add(new Regex(value, id)); + } + } + } catch (Exception e) { + Tuils.sendXMLParseError(context, PATH, e); + return; } } - } catch (Exception e) { - Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + PATH + context.getString(R.string.output_xmlproblem2) + - Tuils.NEWLINE + context.getString(R.string.output_errorlabel) + e.toString()); - return; - } + }.start(); } public static Regex get(int id) { @@ -118,7 +134,7 @@ public class RegexManager { File file = new File(Tuils.getFolder(), PATH); - return XMLPrefsManager.add(file, ROOT, REGEX_LABEL, new String[] {ID_ATTRIBUTE, XMLPrefsManager.VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), value}); + return XMLPrefsManager.add(file, REGEX_LABEL, new String[] {ID_ATTRIBUTE, XMLPrefsManager.VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), value}); } // null: all good @@ -127,7 +143,10 @@ public class RegexManager { try { File file = new File(Tuils.getFolder(), PATH); - Object[] o = XMLPrefsManager.buildDocument(file, ROOT); + Object[] o = XMLPrefsManager.buildDocument(file, null); + if(o == null) { + return null; + } Document d = (Document) o[0]; Element el = (Element) o[1]; @@ -166,10 +185,8 @@ public class RegexManager { Regex regex = get(id); if(regex == null) return Tuils.EMPTYSTRING; - String r = regex.value; - - Pattern p = Pattern.compile(r); - Matcher m = p.matcher(test); + if(regex.regex == null) return "null"; + Matcher m = regex.regex.matcher(test); int color = XMLPrefsManager.getColor(Theme.mark_color); int outputColor = XMLPrefsManager.getColor(Theme.output_color); @@ -189,11 +206,14 @@ public class RegexManager { } public static class Regex { - public String value; + public Pattern regex; + public String literalPattern; public int id; + public Regex() {} + public Regex(String value, int id) { - this.value = value; + this.regex = Pattern.compile(value); this.id = id; } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/RssManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/RssManager.java index 000076a..7375d18 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/RssManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/RssManager.java @@ -5,7 +5,6 @@ import android.content.Intent; import android.graphics.Color; import android.net.ConnectivityManager; import android.os.Handler; -import android.os.Message; import android.text.TextUtils; import org.w3c.dom.Document; @@ -15,9 +14,6 @@ import org.w3c.dom.NodeList; import java.io.BufferedInputStream; import java.io.File; -import java.net.HttpURLConnection; -import java.net.URL; -import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -32,9 +28,15 @@ import javax.xml.parsers.DocumentBuilderFactory; import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; +import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.tuils.InputOutputReceiver; -import ohi.andre.consolelauncher.tuils.TimeManager; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; +import ohi.andre.consolelauncher.tuils.html_escape.HtmlEscape; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.VALUE_ATTRIBUTE; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.set; @@ -51,22 +53,22 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { // etag // last_checked_client + private final int RSS_CHECK_DELAY = 5000; + private final String RSS_FOLDER = "rss"; - public static final String TIME_ATTRIBUTE = "time", SHOW_ATTRIBUTE = "show", URL_ATTRIBUTE = "url", LASTCHECKED_ATTRIBUTE = "lastChecked", LASTMODIFIED_ATTRIBUTE = "lastModified", ETAG_ATTRIBUTE = "etag", + public static final String TIME_ATTRIBUTE = "updateTimeSec", SHOW_ATTRIBUTE = "show", URL_ATTRIBUTE = "url", LASTCHECKED_ATTRIBUTE = "lastChecked", LASTMODIFIED_ATTRIBUTE = "lastModified", ETAG_ATTRIBUTE = "etag", LAST_SHOWN_ITEM_ATTRIBUTE = "lastShownItem", ID_ATTRIBUTE = "id", FORMAT_ATTRIBUTE = "format", INCLUDE_ATTRIBUTE = "includeIfMatches", EXCLUDE_ATTRIBUTE = "excludeIfMatches", - COLOR_ATTRIBUTE = "color", WIFIONLY_ATTRIBUTE = "wifiOnly", COMMAND_ATTRIBUTE = "updateCommand"; - - public static final String RSS_LABEL = "rss", FORMAT_LABEL = "format"; + COLOR_ATTRIBUTE = "color", WIFIONLY_ATTRIBUTE = "wifiOnly", TIME_FORMAT_ATTRIBUTE = "timeFormat", DATE_TAG_ATTRIBUTE = "pubDateTag", + ENTRY_TAG_ATTRIBUTE = "entryTag", ON_ATTRIBUTE = "on", CMD_ATTRIBUTE = "cmd"; - private final String SEPARATOR = " - "; - private final String ID_LABEL = "ID: ", TIME_LABEL = "update time: ", SHOW_LABEL = "show: ", URL_LABEL = "url: "; + public static final String RSS_LABEL = "rss", FORMAT_LABEL = "format", REGEX_CMD_LABEL = "regex"; private final String LAST_MODIFIED_FIELD = "Last-Modified", ETAG_FIELD = "ETag", IF_MODIFIED_SINCE_FIELD = "If-Modified-Since", IF_NONE_MATCH_FIELD = "If-None-Match", GET_LABEL = "GET"; - private final String PUBDATE_CHILD = "pubDate"; + private final String PUBDATE_CHILD = "pubDate", ENTRY_CHILD = "item", LINK_CHILD = "link", HREF_ATTRIBUTE = "href"; - private DateFormat defaultRSSDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z"); + private SimpleDateFormat defaultRSSDateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); private static XMLPrefsManager.XMLPrefsList values; @@ -87,72 +89,83 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { @Override public void write(XMLPrefsManager.XMLPrefsSave save, String value) { - set(new File(Tuils.getFolder(), PATH), NAME, save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); + set(new File(Tuils.getFolder(), PATH), save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); } - private int defaultColor; - private String defaultFormat, timeFormat; - private boolean includeRssDefault; + private int defaultColor, timeColor, downloadMessageColor; + private String defaultFormat, timeFormat, downloadFormat; + + private boolean includeRssDefault, showDownloadMessage, click; private Context context; private Handler handler; - private File root, rssFile; + private File root, rssIndexFile; private List<Rss> feeds; - private List<Format> formats; + private List<XMLPrefsManager.IdValue> formats; + private List<CmdableRegex> cmdRegexes; -// those will obscure the tag and its content + private OkHttpClient client; + + // those will obscure the tag and its content private Pattern[] hideTagPatterns; + private Pattern urlPattern, idPattern, bPattern, kbPattern, mbPattern, gbPattern; + private ConnectivityManager connectivityManager; - public RssManager(Context context) { + public RssManager(Context context, OkHttpClient client) { instance = this; this.context = context; connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - RegexManager.create(context); - root = new File(Tuils.getFolder(), RSS_FOLDER); - rssFile = new File(Tuils.getFolder(), PATH); + rssIndexFile = new File(Tuils.getFolder(), PATH); + + this.client = client; prepare(); values = new XMLPrefsManager.XMLPrefsList(); + handler = new Handler(); refresh(); } public void refresh() { if(handler != null) { handler.removeCallbacksAndMessages(null); - handler = null; } - handler = new Handler() { - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - - - } - }; - if(feeds != null) feeds.clear(); else feeds = new ArrayList<>(); if(formats != null) formats.clear(); else formats = new ArrayList<>(); - new Thread() { + if(cmdRegexes != null) cmdRegexes.clear(); + else cmdRegexes = new ArrayList<>(); + + new StoppableThread() { @Override public void run() { super.run(); + Object[] o; + try { + o = XMLPrefsManager.buildDocument(rssIndexFile, NAME); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return; + } + } catch (Exception e) { + Tuils.sendXMLParseError(context, PATH, e); + return; + } + try { - Object[] o = XMLPrefsManager.buildDocument(rssFile, NAME); Document document = (Document) o[0]; Element rootElement = (Element) o[1]; @@ -164,7 +177,6 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { String[] deleted = instance.deleted(); boolean needToWrite = false; - Main: for(int count = 0; count < nodes.getLength(); count++) { Node node = nodes.item(count); @@ -194,64 +206,47 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { String name = node.getNodeName(); if (name.equals(RSS_LABEL)) { Element t = (Element) node; + feeds.add(Rss.fromElement(t)); + } else if(name.equals(FORMAT_LABEL)) { + Element e = (Element) node; int id; try { - id = Integer.parseInt(t.getAttribute(ID_ATTRIBUTE)); + id = Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)); } catch (Exception exc) { - continue Main; + id = -1; } - String url = t.getAttribute(URL_ATTRIBUTE); - long updateTime = Long.parseLong(t.getAttribute(TIME_ATTRIBUTE)); - boolean show = Boolean.parseBoolean(t.getAttribute(SHOW_ATTRIBUTE)); + if(id == -1) continue; - long lastChecked = t.hasAttribute(LASTCHECKED_ATTRIBUTE) ? Long.parseLong(t.getAttribute(LASTCHECKED_ATTRIBUTE)) : -1; - long lastShown = t.hasAttribute(LAST_SHOWN_ITEM_ATTRIBUTE) ? Long.parseLong(t.getAttribute(LAST_SHOWN_ITEM_ATTRIBUTE)) : -1; - String lastModified = t.hasAttribute(LASTMODIFIED_ATTRIBUTE) ? t.getAttribute(LASTMODIFIED_ATTRIBUTE) : null; - String etag = t.hasAttribute(ETAG_ATTRIBUTE) ? t.getAttribute(ETAG_ATTRIBUTE) : null; + String format = XMLPrefsManager.getStringAttribute(e, XMLPrefsManager.VALUE_ATTRIBUTE); - String format = t.hasAttribute(FORMAT_ATTRIBUTE) ? t.getAttribute(FORMAT_ATTRIBUTE) : null; - String includeIfMatches = t.hasAttribute(INCLUDE_ATTRIBUTE) ? t.getAttribute(INCLUDE_ATTRIBUTE) : null; - String excludeIfMatches = t.hasAttribute(EXCLUDE_ATTRIBUTE) ? t.getAttribute(EXCLUDE_ATTRIBUTE) : null; - int color; - try { - color = Color.parseColor(t.getAttribute(COLOR_ATTRIBUTE)); - } catch (Exception exc) { - color = Integer.MAX_VALUE; - } + XMLPrefsManager.IdValue i = new XMLPrefsManager.IdValue(format, id); + formats.add(i); + } else if(name.equals(REGEX_CMD_LABEL)) { + Element e = (Element) node; - boolean wifiOnly; + int id; try { - wifiOnly = Boolean.parseBoolean(t.getAttribute(WIFIONLY_ATTRIBUTE)); - } catch (Exception e) { - wifiOnly = false; + id = Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)); + } catch (Exception exc) { + continue; } - String updateCommand = t.hasAttribute(COMMAND_ATTRIBUTE) ? t.getAttribute(COMMAND_ATTRIBUTE) : null; - - final Rss r = new Rss(url, updateTime, lastChecked, lastShown, id, show, lastModified, etag, format, includeIfMatches, excludeIfMatches, color, wifiOnly, updateCommand); - feeds.add(r); - } else if(name.equals(FORMAT_LABEL)) { - Element e = (Element) node; + String regex = XMLPrefsManager.getStringAttribute(e, XMLPrefsManager.VALUE_ATTRIBUTE); + if(regex == null || regex.length() == 0) continue; - int id; - try { - id = Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)); - } catch (Exception exc) { - id = -1; - } + String on = XMLPrefsManager.getStringAttribute(e, ON_ATTRIBUTE); + if(on == null || on.length() == 0) continue; - if(id == -1) continue; + String cmd = XMLPrefsManager.getStringAttribute(e, CMD_ATTRIBUTE); + if(cmd == null || cmd.length() == 0) continue; - String format = e.getAttribute(XMLPrefsManager.VALUE_ATTRIBUTE); - - Format i = new Format(format, id); - formats.add(i); - } + cmdRegexes.add(new CmdableRegex(id, on, regex, cmd)); } } } + } if (enums.size() > 0) { for (XMLPrefsManager.XMLPrefsSave s : enums) { @@ -264,19 +259,37 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { values.add(s.label(), value); } - writeTo(document, rssFile); + writeTo(document, rssIndexFile); } else if (needToWrite) { - writeTo(document, rssFile); + writeTo(document, rssIndexFile); } } catch (Exception e) { Tuils.log(e); Tuils.toFile(e); } + click = XMLPrefsManager.getBoolean(ohi.andre.consolelauncher.managers.xml.options.Rss.click_rss); +// longClick = XMLPrefsManager.getBoolean(ohi.andre.consolelauncher.managers.xml.options.Rss.long_click_rss); defaultFormat = XMLPrefsManager.get(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_default_format); defaultColor = XMLPrefsManager.getColor(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_default_color); includeRssDefault = XMLPrefsManager.getBoolean(ohi.andre.consolelauncher.managers.xml.options.Rss.include_rss_default); timeFormat = XMLPrefsManager.get(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_time_format); + showDownloadMessage = XMLPrefsManager.getBoolean(ohi.andre.consolelauncher.managers.xml.options.Rss.show_rss_download); + if(showDownloadMessage) { + downloadFormat = XMLPrefsManager.get(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_download_format); + + String size = "%s"; + + idPattern = Pattern.compile("%id", Pattern.CASE_INSENSITIVE); + urlPattern = Pattern.compile("%url", Pattern.CASE_INSENSITIVE); + gbPattern = Pattern.compile(size + "gb", Pattern.CASE_INSENSITIVE); + mbPattern = Pattern.compile(size + "mb", Pattern.CASE_INSENSITIVE); + kbPattern = Pattern.compile(size + "kb", Pattern.CASE_INSENSITIVE); + bPattern = Pattern.compile(size + "b", Pattern.CASE_INSENSITIVE); + + timeColor = XMLPrefsManager.getColor(Theme.time_color); + downloadMessageColor = XMLPrefsManager.getColor(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_download_message_color); + } String hiddenTags = XMLPrefsManager.get(ohi.andre.consolelauncher.managers.xml.options.Rss.rss_hidden_tags).replaceAll(Tuils.SPACE, Tuils.EMPTYSTRING); String[] split = null; @@ -296,37 +309,49 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } for(Rss rss : feeds) { - rss.updateFormat(); + rss.updateFormat(formats); rss.updateIncludeIfMatches(); rss.updateExcludeIfMatches(); if(rss.color == Integer.MAX_VALUE) rss.color = defaultColor; + } - queue(rss); + for(CmdableRegex rg : cmdRegexes) { + try { + int id = Integer.parseInt(rg.literalPattern); + rg.regex = RegexManager.get(id).regex; + } catch (Exception exc) { + try { + rg.regex = Pattern.compile(rg.literalPattern); + } catch (Exception e) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.invalid_regex) + Tuils.SPACE + rg.literalPattern); + rg.regex = null; + } + } } + + handler.post(updateRunnable); } }.start(); } public void dispose() { - handler.removeCallbacksAndMessages(null); + if(handler != null) handler.removeCallbacksAndMessages(null); } public String add(int id, long timeInSeconds, String url) { - String output = XMLPrefsManager.add(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE, TIME_ATTRIBUTE, SHOW_ATTRIBUTE, URL_ATTRIBUTE}, - new String[] {String.valueOf(id), String.valueOf(timeInSeconds * 1000), String.valueOf(true), url}); + String output = XMLPrefsManager.add(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE, TIME_ATTRIBUTE, SHOW_ATTRIBUTE, URL_ATTRIBUTE}, + new String[] {String.valueOf(id), String.valueOf(timeInSeconds), String.valueOf(true), url}); if(output == null) { try { - Rss r = new Rss(url, timeInSeconds * 1000, id, true); + Rss r = new Rss(url, timeInSeconds, id, true); r.lastShownItem = System.currentTimeMillis(); r.format = defaultFormat; updateRss(r, true); feeds.add(r); - - queue(r); } catch (Exception e) { Tuils.log(e); return e.toString(); @@ -337,15 +362,15 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String addFormat(int id, String value) { - String output = XMLPrefsManager.add(rssFile, NAME, FORMAT_LABEL, new String[] {ID_ATTRIBUTE, XMLPrefsManager.VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), value}); + String output = XMLPrefsManager.add(rssIndexFile, FORMAT_LABEL, new String[] {ID_ATTRIBUTE, XMLPrefsManager.VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), value}); if(output == null) { - formats.add(new Format(value, id)); + formats.add(new XMLPrefsManager.IdValue(value, id)); return null; } else return output; } public String removeFormat(int id) { - String output = XMLPrefsManager.removeNode(rssFile, NAME, FORMAT_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)});; + String output = XMLPrefsManager.removeNode(rssIndexFile, FORMAT_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)});; if(output == null) { return null; } else { @@ -355,7 +380,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String rm(int id) { - String output = XMLPrefsManager.removeNode(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}); + String output = XMLPrefsManager.removeNode(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}); if(output == null) { File rss = new File(root, RSS_LABEL + id + ".xml"); if(rss.exists()) rss.delete(); @@ -374,28 +399,33 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { public String list() { StringBuilder builder = new StringBuilder(); for(Rss r : feeds) { - builder.append(ID_LABEL).append(":").append(Tuils.SPACE).append(r.id).append(Tuils.NEWLINE).append(r.url).append(Tuils.NEWLINE); + builder.append(Rss.ID_LABEL).append(":").append(Tuils.SPACE).append(r.id).append(Tuils.NEWLINE).append(r.url).append(Tuils.NEWLINE); } - return builder.toString().trim(); + String output = builder.toString().trim(); + if(output.length() == 0) return "[]"; + return output; } - public void l(int id) { + public String l(int id) { for(Rss feed : feeds) { if(feed.id == id) { try { parse(feed, false); + return null; } catch (Exception e) { Tuils.log(e); Tuils.toFile(e); + return e.toString(); } - break; } } + + return context.getString(R.string.rss_not_found); } public String setShow(int id, boolean show) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {SHOW_ATTRIBUTE}, new String[] {String.valueOf(show)}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {SHOW_ATTRIBUTE}, new String[] {String.valueOf(show)}, false); if(output == null) { Rss r = findId(id); if(r != null) r.show = show; @@ -408,7 +438,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String setTime(int id, long timeSeconds) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {TIME_ATTRIBUTE}, new String[] {String.valueOf(timeSeconds)}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {TIME_ATTRIBUTE}, new String[] {String.valueOf(timeSeconds)}, false); if(output == null) { Rss r = findId(id); if(r != null) r.updateTimeSeconds = timeSeconds; @@ -420,11 +450,24 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } } + public String setTimeFormat(int id, String format) { + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {TIME_FORMAT_ATTRIBUTE}, new String[] {format}, false); + if(output == null) { + Rss r = findId(id); + if(r != null) r.timeFormat = new SimpleDateFormat(format); + return null; + } + else { + if (output.length() > 0) return output; + return context.getString(R.string.rss_not_found); + } + } + public String setFormat(int id, String format) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {FORMAT_ATTRIBUTE}, new String[] {format}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {FORMAT_ATTRIBUTE}, new String[] {format}, false); if(output == null) { Rss r = findId(id); - if(r != null) r.setFormat(format); + if(r != null) r.setFormat(formats, format); return null; } else { @@ -434,7 +477,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String setColor(int id, String color) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {COLOR_ATTRIBUTE}, new String[] {color}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {COLOR_ATTRIBUTE}, new String[] {color}, false); if(output == null) { Rss r = findId(id); if(r != null) r.color = Color.parseColor(color); @@ -446,8 +489,34 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } } + public String setDateTag(int id, String tag) { + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {DATE_TAG_ATTRIBUTE}, new String[] {tag}, false); + if(output == null) { + Rss r = findId(id); + if(r != null) r.dateTag = tag; + return null; + } + else { + if (output.length() > 0) return output; + return context.getString(R.string.rss_not_found); + } + } + + public String setEntryTag(int id, String tag) { + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {ENTRY_TAG_ATTRIBUTE}, new String[] {tag}, false); + if(output == null) { + Rss r = findId(id); + if(r != null) r.entryTag = tag; + return null; + } + else { + if (output.length() > 0) return output; + return context.getString(R.string.rss_not_found); + } + } + public String setIncludeIfMatches(int id, String regex) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {INCLUDE_ATTRIBUTE}, new String[] {regex}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {INCLUDE_ATTRIBUTE}, new String[] {regex}, false); if(output == null) { Rss r = findId(id); if(r != null) r.setIncludeIfMatches(regex); @@ -460,7 +529,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String setExcludeIfMatches(int id, String regex) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {EXCLUDE_ATTRIBUTE}, new String[] {regex}, false); + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {EXCLUDE_ATTRIBUTE}, new String[] {regex}, false); if(output == null) { Rss r = findId(id); if(r != null) r.setExcludeIfMatches(regex); @@ -473,7 +542,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } public String setWifiOnly(int id, boolean wifiOnly) { - String output = XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {WIFIONLY_ATTRIBUTE}, new String[] {String.valueOf(wifiOnly)}, + String output = XMLPrefsManager.set(rssIndexFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {WIFIONLY_ATTRIBUTE}, new String[] {String.valueOf(wifiOnly)}, false); if(output == null) { Rss r = findId(id); @@ -486,121 +555,177 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } } -// base methods + public String addRegexCommand(int id, String on, String regex, String cmd) { + String output = XMLPrefsManager.add(rssIndexFile, REGEX_CMD_LABEL, new String[] {ID_ATTRIBUTE, ON_ATTRIBUTE, XMLPrefsManager.VALUE_ATTRIBUTE, CMD_ATTRIBUTE}, + new String[] {String.valueOf(id), on, regex, cmd}); + if(output == null) { + cmdRegexes.add(new CmdableRegex(id, on, regex, cmd)); + return null; + } else { + if(output.length() > 0) return output; + return context.getString(R.string.output_error); + } + } - private void queue(final Rss rss) { - Runnable rn = new Runnable() { - @Override - public void run() { - try { - updateRss(rss, false); - queue(rss); - } catch (Exception e1) { - Tuils.log(e1); - } + public String rmRegexCommand(int id) { + String output = XMLPrefsManager.removeNode(rssIndexFile, REGEX_CMD_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}); + if(output == null) { + for(int i = 0; i < cmdRegexes.size(); i++) { + if(cmdRegexes.get(i).id == id) cmdRegexes.remove(i); } - }; - - long delay; - if(rss.lastCheckedClient == -1) delay = 0; - else delay = rss.updateTimeSeconds - (System.currentTimeMillis() - rss.lastCheckedClient); - if(delay <= 0) rn.run(); - else { - handler.postDelayed(rn, delay); + return null; + } else { + if(output.length() > 0) return output; + return context.getString(R.string.id_notfound); } } +// base methods + + private Runnable updateRunnable = new Runnable() { + @Override + public void run() { + for(Rss feed : feeds) { + if(feed.needUpdate()) updateRss(feed, false); + } + + handler.postDelayed(this, RSS_CHECK_DELAY); + } + }; + final String quotes = "\""; private void updateRss(final Rss feed, final boolean firstTime) { - if(feed.wifiOnly && !connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) { + updateRss(feed, firstTime, false); + } + + public boolean updateRss(int feed, final boolean firstTime, boolean force) { + Rss rss = findId(feed); + if(rss == null) return false; + + updateRss(rss, firstTime, force); + return true; + } + + private void updateRss(final Rss feed, final boolean firstTime, boolean force) { + if(!force && feed.wifiOnly && !connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) { feed.lastCheckedClient = System.currentTimeMillis(); - feed.updateFile(); + feed.updateFile(rssIndexFile); return; } - final Object o = new Object(); - - new Thread() { + new StoppableThread() { @Override public void run() { super.run(); + if(!Tuils.hasInternetAccess()) { + return; + } + try { - URL uObj = new URL(feed.url); - HttpURLConnection urlConnection = (HttpURLConnection) uObj.openConnection(); - urlConnection.setRequestMethod(GET_LABEL); + Request.Builder builder = new Request.Builder() + .url(feed.url) + .get(); - if(!firstTime) { - urlConnection.setRequestProperty(IF_MODIFIED_SINCE_FIELD, feed.lMod); - urlConnection.setRequestProperty(IF_NONE_MATCH_FIELD, quotes + feed.etag + quotes); + if(!firstTime && feed.lMod != null && feed.etag != null) { + builder.addHeader(IF_MODIFIED_SINCE_FIELD, feed.lMod); + builder.addHeader(IF_NONE_MATCH_FIELD, quotes + feed.etag + quotes); } - urlConnection.connect(); + Response response = client.newCall(builder.build()).execute(); - if(firstTime || urlConnection.getResponseCode() != 304) { - Tuils.download(new BufferedInputStream(uObj.openStream()), new File(root, RSS_LABEL + feed.id + ".xml")); + if(firstTime || response.code() != 304) { + ResponseBody body = response.body(); - feed.lMod = urlConnection.getHeaderField(LAST_MODIFIED_FIELD); - feed.etag = urlConnection.getHeaderField(ETAG_FIELD); - if(feed.etag != null) feed.etag = feed.etag.replaceAll("\"", Tuils.EMPTYSTRING); + long bytes = 0; + if(body != null) bytes = Tuils.download(new BufferedInputStream(body.byteStream()), new File(root, RSS_LABEL + feed.id + ".xml")); + + if(showDownloadMessage) { + CharSequence c = Tuils.span(downloadFormat, downloadMessageColor); + + double kb = (double) bytes / (double) 1024; + double mb = kb / (double) 1024; + double gb = mb / (double) 1024; + + kb = Tuils.round(kb, 2); + mb = Tuils.round(mb, 2); + gb = Tuils.round(gb, 2); + + c = urlPattern.matcher(c).replaceAll(feed.url); + c = idPattern.matcher(c).replaceAll(String.valueOf(feed.id)); + c = gbPattern.matcher(c).replaceAll(String.valueOf(gb)); + c = mbPattern.matcher(c).replaceAll(String.valueOf(mb)); + c = kbPattern.matcher(c).replaceAll(String.valueOf(kb)); + c = bPattern.matcher(c).replaceAll(String.valueOf(bytes)); - if(parse(feed, true) && feed.updateCommand != null) { - Intent intent = new Intent(InputOutputReceiver.ACTION_CMD); - intent.putExtra(InputOutputReceiver.SHOW_CONTENT, false); - intent.putExtra(InputOutputReceiver.TEXT, feed.updateCommand); - context.sendBroadcast(intent); + c = TimeManager.replace(c, timeColor); + + Tuils.sendOutput(downloadMessageColor, context, c); + } + + if(bytes == 0) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.rss_invalid_empty) + Tuils.SPACE + feed.id); + return; } + + feed.lMod = response.header(LAST_MODIFIED_FIELD); + feed.etag = response.header(ETAG_FIELD); + if(feed.etag != null) feed.etag = feed.etag.replaceAll("\"", Tuils.EMPTYSTRING); + + response.close(); + + if(feed.show) parse(feed, true); } else { // not modified } feed.lastCheckedClient = System.currentTimeMillis(); - feed.updateFile(); + feed.updateFile(rssIndexFile); } catch (Exception e) { Tuils.log(e); Tuils.toFile(e); } - - synchronized (o) { - o.notify(); - } } }.start(); - - synchronized (o) { - try { - o.wait(); - } catch (InterruptedException e) { - Tuils.log(e); - Tuils.toFile(e); - } - } } private boolean parse(Rss feed, boolean time) throws Exception { - Tuils.log("parse"); - boolean updated = false; File rssFile = new File(root, RSS_LABEL + feed.id + ".xml"); - if(!rssFile.exists()) return updated; + if(!rssFile.exists()) return false; DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - Document doc = dBuilder.parse(rssFile); + + Document doc; + try { + doc = dBuilder.parse(rssFile); + } catch (Exception e) { + Tuils.sendXMLParseError(context, PATH, e); + return false; + } doc.getDocumentElement().normalize(); - long lastShown = feed.lastShownItem; + long greatestTime = -1; + boolean foundOneDateAtLeast = false; + + String entryTag = feed.entryTag != null ? feed.entryTag : ENTRY_CHILD; + String dateTag = feed.dateTag != null ? feed.dateTag : PUBDATE_CHILD; + + NodeList list = doc.getElementsByTagName(entryTag); + if(list.getLength() == 0) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.rss_invalid_entry_tag) + Tuils.SPACE + (entryTag)); + return false; + } - NodeList list = doc.getElementsByTagName("item"); for(int c = list.getLength(); c >= 0; c--) { - Tuils.log("index: " + c); Element element = (Element) list.item(c); if(element == null) { @@ -608,46 +733,60 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } if(time) { - Tuils.log("timing"); - NodeList l = element.getElementsByTagName(PUBDATE_CHILD); + NodeList l = element.getElementsByTagName(dateTag); if(l.getLength() == 0) continue; + foundOneDateAtLeast = true; + String date = l.item(0).getTextContent(); - Date d = defaultRSSDateFormat.parse(date); - long timeLong = d.getTime(); - if(c == 0) { - feed.lastShownItem = d.getTime(); - feed.updateTime(); + Date d; + try { + d = feed.timeFormat != null ? feed.timeFormat.parse(date) : defaultRSSDateFormat.parse(date); + } catch (Exception e) { + Tuils.sendOutput(Color.RED, context, rssFile.getName() + ": " + context.getString(R.string.rss_invalid_timeformat)); + return false; } - Tuils.log("pubDate: " + timeLong); - Tuils.log("lastShown: " + lastShown); + long timeLong = d.getTime(); + greatestTime = Math.max(greatestTime, timeLong); - if(lastShown < timeLong) { + if(feed.lastShownItem < timeLong) { updated = true; - - Tuils.log("showing"); - showItem(feed, element); + showItem(feed, element, false); } } else { +// user - requested updated = true; - - Tuils.log("not timing"); - showItem(feed, element); + showItem(feed, element, true); } } + if(time && !foundOneDateAtLeast) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.rss_invalid_date) + Tuils.SPACE + (dateTag)); + } else if(greatestTime != -1) { + feed.lastShownItem = greatestTime; + feed.updateTime(rssIndexFile); + } + return updated; } - private final Pattern formatPattern = Pattern.compile("%(?:\\[(\\d+)\\])?(?:\\[([^]]+)\\])?([^\\s]+)"); + private final Pattern formatPattern = Pattern.compile("%(?:\\[(\\d+)\\])?(?:\\[([^]]+)\\])?([a-zA-Z]+)"); private final Pattern removeTags = Pattern.compile("<[^>]+>"); private final String THREE_DOTS = "..."; + private final String OPEN_URL = "search -u "; + private final String PERCENTAGE = "%"; + + private Pattern nl = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); + // called when a new element is detected, it could be triggered many times again in some milliseconds - private void showItem(Rss feed, Element item) { + private void showItem(Rss feed, Element item, boolean userRequested) { + if(item == null) return; + String cp = feed.format != null ? feed.format : defaultFormat; + cp = nl.matcher(cp).replaceAll(Tuils.NEWLINE); CharSequence s = Tuils.span(cp, feed.color); @@ -665,19 +804,23 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { if(ls.getLength() == 0) value = Tuils.EMPTYSTRING; else { value = ls.item(0).getTextContent(); + if(value != null) value = value.trim(); + else value = Tuils.EMPTYSTRING; - if(value.equals(PUBDATE_CHILD)) { + if(feed.dateTag == null ? tag.equals(PUBDATE_CHILD) : (tag.equals(PUBDATE_CHILD) || tag.equals(feed.dateTag))) { Date d; try { - d = defaultRSSDateFormat.parse(value); + d = feed.timeFormat != null ? feed.timeFormat.parse(value) : defaultRSSDateFormat.parse(value); } catch (ParseException e) { + Tuils.log(e); continue; } long timeLong = d.getTime(); value = TimeManager.replace(timeFormat, timeLong, Integer.MAX_VALUE).toString(); - } else { + value = HtmlEscape.unescapeHtml(value); + for(Pattern p : hideTagPatterns) { value = p.matcher(value).replaceAll(Tuils.EMPTYSTRING); } @@ -689,7 +832,6 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { value = value.substring(0, l); value = value.concat(THREE_DOTS); } catch (Exception e) {} - } try { @@ -697,7 +839,6 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } catch (Exception e) { cl = feed.color; } - } CharSequence replace; @@ -709,49 +850,143 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { s = TextUtils.replace(s, new String[] {m.group(0)}, new CharSequence[] {replace}); } - } if(includeRssDefault) { - if(feed.excludeIfMatches != null && feed.excludeIfMatches.length() > 0) { - Pattern p = Pattern.compile(feed.excludeIfMatches); - if (p.matcher(s.toString()).find()) { - return; - } - } + if(feed.excludeIfMatches != null && feed.excludeIfMatches.matcher(s.toString()).find()) return; } else { - if(feed.includeIfMatches != null && feed.includeIfMatches.length() > 0) { - Pattern p = Pattern.compile(feed.includeIfMatches); - if (!p.matcher(s.toString()).find()) { - return; + if(feed.includeIfMatches != null && feed.includeIfMatches.matcher(s.toString()).find()) return; + } + + if(!userRequested) { + for(CmdableRegex r : cmdRegexes) { + if(r.regex == null || !Tuils.arrayContains(r.on, feed.id)) continue; + + String cmd = r.cmd; + + Matcher rssMatcher = r.regex.matcher(s.toString()); + if(rssMatcher.matches() || rssMatcher.find()) { + + for(int c = 1; c < rssMatcher.groupCount() + 1; c++) { + cmd = cmd.replaceAll(PERCENTAGE + c, rssMatcher.group(c)); + } + + Intent intent = new Intent(InputOutputReceiver.ACTION_CMD); + intent.putExtra(InputOutputReceiver.SHOW_CONTENT, false); + intent.putExtra(InputOutputReceiver.TEXT, cmd); + context.sendBroadcast(intent); } } } - Tuils.sendOutput(context, s); + String url = null; + NodeList list = item.getElementsByTagName(LINK_CHILD); + if(list.getLength() != 0) { + Node n = list.item(0); + url = n.getTextContent(); + + if(n.getNodeType() == Node.ELEMENT_NODE && (url == null || url.length() == 0)) { + url = ((Element) n).getAttribute(HREF_ATTRIBUTE); + } + } + + String action; + if(url == null || url.length() == 0) action = null; + else action = OPEN_URL + url; + + Tuils.sendOutput(context, s, TerminalManager.CATEGORY_RSS, click ? action : null); } - private class Rss { - String url; - long updateTimeSeconds, lastCheckedClient, lastShownItem; - int id; - boolean show; - String lMod, etag; + public static class Rss { + private static final String ID_LABEL = "ID", URL_LABEL = "URL", UPDATE_TIME_LABEL = "update time", SHOW_LABEL = "show"; + + public String url; + public long updateTimeSeconds, lastCheckedClient, lastShownItem; + public int id; + public boolean show; + public String lMod, etag; + + public String entryTag, dateTag; + + public String format; - String format; - String includeIfMatches, excludeIfMatches; - String updateCommand; + public Pattern includeIfMatches, excludeIfMatches; + public String tempInclude, tempExclude; - int color; + public int color; - boolean wifiOnly; + public long millisecondsLeft, lastUpdated; + + public boolean wifiOnly; + + public SimpleDateFormat timeFormat; public Rss(String url, long updateTimeSeconds, int id, boolean show) { - this(url, updateTimeSeconds, -1, -1, id, show, null, null, null, null, null, Integer.MAX_VALUE, false, null); + this(url, updateTimeSeconds, -1, -1, id, show, null, null, null, null, null, Integer.MAX_VALUE, false, null, null, null); } public Rss(String url, long updateTimeSeconds, long lastCheckedClient, long lastShownItem, int id, boolean show, String lMod, String etag, String format, - String includeIfMatches, String excludeIfMatches, int color, boolean wifiOnly, String updateCommand) { + String includeIfMatches, String excludeIfMatches, int color, boolean wifiOnly, String timeFormat, String rootNode, String timeNode) { + setAll(url, updateTimeSeconds, lastCheckedClient, lastShownItem, id, show, lMod, etag, format, includeIfMatches, excludeIfMatches, color, wifiOnly, timeFormat, rootNode, timeNode); + + millisecondsLeft = updateTimeSeconds * 1000 - (System.currentTimeMillis() - lastCheckedClient); + } + + public static Rss fromElement(Element t) { + int id; + try { + id = Integer.parseInt(t.getAttribute(ID_ATTRIBUTE)); + } catch (Exception exc) { + return null; + } + + String url = t.getAttribute(URL_ATTRIBUTE); + if(url == null) return null; + + long updateTime; + try { + updateTime = Long.parseLong(t.getAttribute(TIME_ATTRIBUTE)); + } catch (Exception e) { +// default: 1/2 h + updateTime = 60 * 30; + } + + boolean show = true; + try { + show = Boolean.parseBoolean(t.getAttribute(SHOW_ATTRIBUTE)); + } catch (Exception e) {} + + long lastChecked = XMLPrefsManager.getLongAttribute(t, LASTCHECKED_ATTRIBUTE); + long lastShown = XMLPrefsManager.getLongAttribute(t, LAST_SHOWN_ITEM_ATTRIBUTE); + + String lastModified = XMLPrefsManager.getStringAttribute(t, LASTMODIFIED_ATTRIBUTE); + String etag = XMLPrefsManager.getStringAttribute(t, ETAG_ATTRIBUTE); + + String format = XMLPrefsManager.getStringAttribute(t, FORMAT_ATTRIBUTE); + String includeIfMatches = XMLPrefsManager.getStringAttribute(t, INCLUDE_ATTRIBUTE); + String excludeIfMatches = XMLPrefsManager.getStringAttribute(t, EXCLUDE_ATTRIBUTE); + int color; + try { + color = Color.parseColor(t.getAttribute(COLOR_ATTRIBUTE)); + } catch (Exception exc) { + color = Integer.MAX_VALUE; + } + + boolean wifiOnly = false; + try { + wifiOnly = Boolean.parseBoolean(t.getAttribute(WIFIONLY_ATTRIBUTE)); + } catch (Exception e) {} + + String timeFormat = XMLPrefsManager.getStringAttribute(t, TIME_FORMAT_ATTRIBUTE); + + String rootNode = XMLPrefsManager.getStringAttribute(t, ENTRY_TAG_ATTRIBUTE); + String timeNode = XMLPrefsManager.getStringAttribute(t, DATE_TAG_ATTRIBUTE); + + return new Rss(url, updateTime, lastChecked, lastShown, id, show, lastModified, etag, format, includeIfMatches, excludeIfMatches, color, wifiOnly, timeFormat, rootNode, timeNode); + } + + private void setAll(String url, long updateTimeSeconds, long lastCheckedClient, long lastShownItem, int id, boolean show, String lMod, String etag, String format, + String includeIfMatches, String excludeIfMatches, int color, boolean wifiOnly, String timeFormat, String rootNode, String timeNode) { this.url = url; this.updateTimeSeconds = updateTimeSeconds; this.lastShownItem = lastShownItem; @@ -762,48 +997,77 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { this.etag = etag; this.format = format; - this.includeIfMatches = includeIfMatches; - this.excludeIfMatches = excludeIfMatches; + + this.tempInclude = includeIfMatches; + this.tempExclude = excludeIfMatches; this.color = color; this.wifiOnly = wifiOnly; - this.updateCommand = updateCommand; + if(timeFormat == null) this.timeFormat = null; + else try { + this.timeFormat = new SimpleDateFormat(timeFormat); + } catch (Exception e) { + this.timeFormat = null; + } + + this.entryTag = rootNode; + this.dateTag = timeNode; } - private void updateFile() { - XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, - new String[] {LASTCHECKED_ATTRIBUTE, LASTMODIFIED_ATTRIBUTE, ETAG_ATTRIBUTE}, new String[] {String.valueOf(lastCheckedClient), lMod, etag}, - false); + public boolean needUpdate() { + millisecondsLeft -= System.currentTimeMillis() - lastUpdated; + + lastUpdated = System.currentTimeMillis(); + + if(millisecondsLeft <= 0) { + millisecondsLeft = updateTimeSeconds * 1000; + + return true; + } + + return false; } - @Override - public String toString() { - return new StringBuilder().append(ID_LABEL).append(id).append(SEPARATOR).append(TIME_LABEL).append(updateTimeSeconds).append(SEPARATOR).append(SHOW_LABEL).append(show).append(Tuils.NEWLINE) - .append(URL_LABEL).append(url).toString(); + public void updateTime(File rssFile) { + XMLPrefsManager.set(rssFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {LAST_SHOWN_ITEM_ATTRIBUTE}, + new String[] {String.valueOf(lastShownItem)}, false); } - public void setFormat(String format) { - this.format = format; - updateFormat(); + public void updateIncludeIfMatches() { + if(includeIfMatches != null) { + try { + int id = Integer.parseInt(tempInclude); + includeIfMatches = RegexManager.get(id).regex; + } catch (Exception exc) { + includeIfMatches = Pattern.compile(tempInclude); + } + } } - public void setIncludeIfMatches(String includeIfMatches) { - this.includeIfMatches = includeIfMatches; - updateIncludeIfMatches(); + public void updateExcludeIfMatches() { + if(excludeIfMatches != null) { + try { + int id = Integer.parseInt(tempExclude); + excludeIfMatches = RegexManager.get(id).regex; + } catch (Exception exc) { + includeIfMatches = Pattern.compile(tempExclude); + } + } } - public void setExcludeIfMatches(String excludeIfMatches) { - this.excludeIfMatches = excludeIfMatches; - updateExcludeIfMatches(); + private void updateFile(File rssFile) { + XMLPrefsManager.set(rssFile, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, + new String[] {LASTCHECKED_ATTRIBUTE, LASTMODIFIED_ATTRIBUTE, ETAG_ATTRIBUTE}, new String[] {String.valueOf(lastCheckedClient), lMod, etag}, + false); } - public void updateFormat() { + public void updateFormat(List<XMLPrefsManager.IdValue> formats) { if(format != null) { try { int id = Integer.parseInt(format); - for(Format i : formats) { + for(XMLPrefsManager.IdValue i : formats) { if(id == i.id) format = i.value; } } catch (Exception exc) { @@ -812,22 +1076,19 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } } - public void updateIncludeIfMatches() { - if(includeIfMatches != null) { - try { - int id = Integer.parseInt(includeIfMatches); - includeIfMatches = RegexManager.get(id).value; - } catch (Exception exc) {} - } + public void setFormat(List<XMLPrefsManager.IdValue> formats, String format) { + this.format = format; + updateFormat(formats); } - public void updateExcludeIfMatches() { - if(excludeIfMatches != null) { - try { - int id = Integer.parseInt(excludeIfMatches); - excludeIfMatches = RegexManager.get(id).value; - } catch (Exception exc) {} - } + public void setIncludeIfMatches(String includeIfMatches) { + tempInclude = includeIfMatches; + updateIncludeIfMatches(); + } + + public void setExcludeIfMatches(String excludeIfMatches) { + tempExclude = excludeIfMatches; + updateExcludeIfMatches(); } @Override @@ -837,19 +1098,14 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { return false; } - public void updateTime() { - Tuils.log("updating time: " + XMLPrefsManager.set(rssFile, NAME, RSS_LABEL, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}, new String[] {LAST_SHOWN_ITEM_ATTRIBUTE}, - new String[] {String.valueOf(lastShownItem)}, false)); - } - } - - private static class Format { - String value; - int id; - - public Format(String value, int id) { - this.value = value; - this.id = id; + @Override + public String toString() { + String dots = ":"; + return new StringBuilder().append(ID_LABEL).append(dots).append(Tuils.SPACE).append(id).append(Tuils.SPACE) + .append(URL_LABEL).append(dots).append(Tuils.SPACE).append(url).append(Tuils.SPACE) + .append(UPDATE_TIME_LABEL).append(dots).append(Tuils.SPACE).append(updateTimeSeconds).append(Tuils.SPACE) + .append(SHOW_LABEL).append(dots).append(Tuils.SPACE).append(show) + .toString(); } } @@ -863,7 +1119,7 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { } } - private Rss findId(int id) { + public Rss findId(int id) { for(int c = 0; c < feeds.size(); c++) { Rss r = feeds.get(c); if(r.id == id) { @@ -880,10 +1136,10 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { check = false; root.mkdir(); } - if(!rssFile.exists()) { + if(!rssIndexFile.exists()) { try { - rssFile.createNewFile(); - XMLPrefsManager.resetFile(rssFile, NAME); + rssIndexFile.createNewFile(); + XMLPrefsManager.resetFile(rssIndexFile, NAME); check = false; return check && root.list().length > 1; @@ -894,4 +1150,45 @@ public class RssManager implements XMLPrefsManager.XmlPrefsElement { return check; } + + private class CmdableRegex extends RegexManager.Regex { + int[] on; + String cmd; + + public CmdableRegex(int id, String on, String regex, String cmd) { + this.id = id; + + this.literalPattern = regex; + this.cmd = cmd; + + char separator = Tuils.firstNonDigit(on); + if(separator == 0) { + try { + this.on = new int[] {Integer.parseInt(on)}; + } catch (Exception e) { + Tuils.log(e); + } + } else { + if(separator == ' ') { + char s2 = Tuils.firstNonDigit(Tuils.removeSpaces(on)); + if(s2 != 0) { + on = Tuils.removeSpaces(on); + separator = s2; + } + } + + String[] split = on.split(Pattern.quote(separator + Tuils.EMPTYSTRING)); + this.on = new int[split.length]; + + for(int c = 0; c < split.length; c++) { + try { + this.on[c] = Integer.parseInt(split[c]); + } catch (Exception e) { + Tuils.log(e); + this.on[c] = Integer.MAX_VALUE; + } + } + } + } + } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalManager.java index 86025ad..ae96775 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalManager.java @@ -2,14 +2,15 @@ package ohi.andre.consolelauncher.managers; import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.graphics.Color; import android.os.IBinder; -import android.text.InputFilter; import android.text.InputType; import android.text.Layout; +import android.text.Spannable; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; -import android.text.method.ScrollingMovementMethod; import android.text.style.ForegroundColorSpan; import android.view.KeyEvent; import android.view.View; @@ -26,11 +27,14 @@ import java.util.List; import ohi.andre.consolelauncher.UIManager; import ohi.andre.consolelauncher.commands.main.MainPack; import ohi.andre.consolelauncher.commands.main.raw.clear; +import ohi.andre.consolelauncher.managers.notifications.KeeperService; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Behavior; import ohi.andre.consolelauncher.managers.xml.options.Theme; import ohi.andre.consolelauncher.managers.xml.options.Ui; -import ohi.andre.consolelauncher.tuils.TimeManager; +import ohi.andre.consolelauncher.tuils.InputOutputReceiver; +import ohi.andre.consolelauncher.tuils.LongClickMovementMethod; +import ohi.andre.consolelauncher.tuils.LongClickableSpan; import ohi.andre.consolelauncher.tuils.Tuils; import ohi.andre.consolelauncher.tuils.interfaces.Rooter; @@ -56,7 +60,6 @@ public class TerminalManager { public static final int CATEGORY_INPUT = 10; public static final int CATEGORY_OUTPUT = 11; public static final int CATEGORY_NOTIFICATION = 12; - public static final int CATEGORY_GENERAL = 13; public static final int CATEGORY_RSS = 14; private long lastEnter; @@ -89,7 +92,7 @@ public class TerminalManager { private MainPack mainPack; - private boolean defaultHint = true, autoLowerFirstChar; + private boolean defaultHint = true; private int clearCmdsCount= 0; @@ -103,8 +106,10 @@ public class TerminalManager { } }; - private String inputFormat; - private String outputFormat; + private String inputFormat, outputFormat; + private int inputColor, outputColor; + + private boolean keeperServiceRunning, clickCommands, longClickCommands; private Context mContext; @@ -117,7 +122,8 @@ public class TerminalManager { this.mainPack = mainPack; - this.autoLowerFirstChar = XMLPrefsManager.getBoolean(Behavior.autolower_firstchar); + this.clickCommands = XMLPrefsManager.getBoolean(Behavior.click_commands); + this.longClickCommands = XMLPrefsManager.getBoolean(Behavior.long_click_commands); this.clearAfterMs = XMLPrefsManager.getInt(Behavior.clear_after_seconds) * 1000; this.clearAfterCmds = XMLPrefsManager.getInt(Behavior.clear_after_cmds); @@ -126,11 +132,16 @@ public class TerminalManager { inputFormat = XMLPrefsManager.get(Behavior.input_format); outputFormat = XMLPrefsManager.get(Behavior.output_format); + inputColor = XMLPrefsManager.getColor(Theme.input_color); + outputColor = XMLPrefsManager.getColor(Theme.output_color); + prefix = XMLPrefsManager.get(Ui.input_prefix); suPrefix = XMLPrefsManager.get(Ui.input_root_prefix); int ioSize = XMLPrefsManager.getInt(Ui.input_output_size); + keeperServiceRunning = XMLPrefsManager.getBoolean(Behavior.tui_notification); + prefixView.setTypeface(Tuils.getTypeface(context)); prefixView.setTextColor(XMLPrefsManager.getColor(Theme.input_color)); prefixView.setTextSize(ioSize); @@ -195,7 +206,7 @@ public class TerminalManager { this.mTerminalView.setTypeface(Tuils.getTypeface(context)); this.mTerminalView.setTextSize(ioSize); this.mTerminalView.setFocusable(false); - setupScroller(); + this.mTerminalView.setMovementMethod(LongClickMovementMethod.getInstance(XMLPrefsManager.getInt(Behavior.long_click_duration))); if(clearAfterMs > 0) this.mTerminalView.postDelayed(clearRunnable, clearAfterMs); if(maxLines > 0) { @@ -240,6 +251,8 @@ public class TerminalManager { this.mInputView.setTypeface(Tuils.getTypeface(context)); this.mInputView.setHint(Tuils.getHint(mainPack.currentDirectory.getAbsolutePath())); this.mInputView.setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); + Tuils.setCursorDrawableColor(this.mInputView, XMLPrefsManager.getColor(Theme.cursor_color)); + this.mInputView.setHighlightColor(Color.TRANSPARENT); this.mInputView.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override @@ -263,26 +276,24 @@ public class TerminalManager { onNewInput(); } -// if(event == null && actionId == EditorInfo.IME_NULL) onNewInput(); +// if (event == null && actionId == EditorInfo.IME_NULL) onNewInput(); // if (event != null && event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) onNewInput(); return true; } }); - if(autoLowerFirstChar) { - this.mInputView.setFilters(new InputFilter[] {new InputFilter() { - @Override - public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - if (dstart == 0 && dend == 0 && start == 0 && end == 1) { - if(source.length() > 0) { - return TextUtils.concat(source.toString().toLowerCase().charAt(0) + Tuils.EMPTYSTRING, source.subSequence(1,source.length())); - } - } - - return source; - } - }}); - } +// if(autoLowerFirstChar) { +// this.mInputView.setFilters(new InputFilter[] {new InputFilter() { +// @Override +// public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { +// if (dstart == 0 && dend == 0 && start == 0 && end == 1 && source.length() > 0) { +// return TextUtils.concat(source.toString().toLowerCase().charAt(0) + Tuils.EMPTYSTRING, source.subSequence(1,source.length())); +// } +// +// return source; +// } +// }}); +// } } private void setupNewInput() { @@ -300,7 +311,15 @@ public class TerminalManager { return false; } - String input = mInputView.getText().toString().trim(); + CharSequence input = mInputView.getText(); + + String cmd = input.toString().trim(); + Object obj = null; + try { + obj = ((Spannable) input).getSpans(0, input.length(), AppsManager.LaunchInfo.class)[0]; + } catch (Exception e) { +// an error will probably be thrown everytime, but we don't need to track it + } if(input.length() > 0) { clearCmdsCount++; @@ -314,13 +333,13 @@ public class TerminalManager { if(cmdList.size() == CMD_LIST_SIZE) { cmdList.remove(0); } - cmdList.add(cmdList.size(), input); + cmdList.add(cmdList.size(), cmd); howBack = -1; } - +// I tried to use intents and ioreceiver.class instead, but it wasn't better if (mInputListener != null) { - mInputListener.onNewInput(input); + mInputListener.onNewInput(cmd, obj); } setupNewInput(); @@ -387,12 +406,19 @@ public class TerminalManager { } } - final String FORMAT_INPUT = "%i"; - final String FORMAT_OUTPUT = "%o"; - final String FORMAT_PREFIX = "%p"; - final String FORMAT_NEWLINE = "%n"; + public static final String FORMAT_INPUT = "%i"; + public static final String FORMAT_OUTPUT = "%o"; + public static final String FORMAT_PREFIX = "%p"; + public static final String FORMAT_NEWLINE = "%n"; private void writeToView(CharSequence text, int type) { + if(type == CATEGORY_INPUT && keeperServiceRunning) { + Intent i = new Intent(mContext, KeeperService.class); + i.putExtra(KeeperService.CMD_KEY, text.toString()); + i.putExtra(KeeperService.PATH_KEY, mainPack.currentDirectory.getAbsolutePath()); + mContext.startService(i); + } + text = getFinalText(text, type); text = TextUtils.concat(Tuils.NEWLINE, text); writeToView(text); @@ -416,26 +442,28 @@ public class TerminalManager { boolean su = t.toString().startsWith("su ") || suMode; - SpannableString si = Tuils.span(inputFormat, XMLPrefsManager.getColor(Theme.input_color)); + SpannableString si = Tuils.span(inputFormat, inputColor); + if(clickCommands || longClickCommands) si.setSpan(new LongClickableSpan(clickCommands ? t.toString() : null, longClickCommands ? t.toString() : null, InputOutputReceiver.ACTION_INPUT), 0, + si.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); s = TimeManager.replace(si,XMLPrefsManager.getColor(Theme.time_color)); s = TextUtils.replace(s, - new String[] {FORMAT_INPUT, FORMAT_PREFIX, FORMAT_NEWLINE, - FORMAT_INPUT.toUpperCase(), FORMAT_PREFIX.toUpperCase(), FORMAT_NEWLINE.toUpperCase()}, + new String[] {FORMAT_INPUT, FORMAT_PREFIX, FORMAT_NEWLINE, FORMAT_INPUT.toUpperCase(), FORMAT_PREFIX.toUpperCase(), FORMAT_NEWLINE.toUpperCase()}, new CharSequence[] {t, su ? suPrefix : prefix, Tuils.NEWLINE, t, su ? suPrefix : prefix, Tuils.NEWLINE}); break; case CATEGORY_OUTPUT: t = t.toString().trim(); - SpannableString so = Tuils.span(outputFormat, XMLPrefsManager.getColor(Theme.output_color)); + SpannableString so = Tuils.span(outputFormat, outputColor); s = TextUtils.replace(so, new String[] {FORMAT_OUTPUT, FORMAT_NEWLINE, FORMAT_OUTPUT.toUpperCase(), FORMAT_NEWLINE.toUpperCase()}, new CharSequence[] {t, Tuils.NEWLINE, t, Tuils.NEWLINE}); break; - case CATEGORY_NOTIFICATION: case CATEGORY_GENERAL:case CATEGORY_RSS: +// already colored here + case CATEGORY_NOTIFICATION:case CATEGORY_RSS: s = t; break; default: @@ -449,19 +477,24 @@ public class TerminalManager { onNewInput(); } - public void setupScroller() { - this.mTerminalView.setMovementMethod(new ScrollingMovementMethod()); - } - public String getInput() { return mInputView.getText().toString(); } - public void setInput(String input) { - mInputView.setText(input); + public void setInput(String input, Object obj) { + SpannableString spannable = new SpannableString(input); + if(obj != null) { + spannable.setSpan(obj, 0, input.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + mInputView.setText(spannable); focusInputEnd(); } + public void setInput(String input) { + setInput(input, null); + } + public void setHint(String hint) { defaultHint = false; diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/ThemesManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/ThemesManager.java index 63f3223..95a4ed5 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/ThemesManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/ThemesManager.java @@ -140,6 +140,8 @@ public class ThemesManager { return; } + if(o == null) return; + Document d = (Document) o[0]; Element root = (Element) o[1]; diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/TimeManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/TimeManager.java similarity index 68% rename from app/src/main/java/ohi/andre/consolelauncher/tuils/TimeManager.java rename to app/src/main/java/ohi/andre/consolelauncher/managers/TimeManager.java index 8541399..22addaa 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/TimeManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/TimeManager.java @@ -1,19 +1,20 @@ -package ohi.andre.consolelauncher.tuils; +package ohi.andre.consolelauncher.managers; import android.content.Context; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; -import android.text.format.Time; import android.text.style.AbsoluteSizeSpan; import android.text.style.ForegroundColorSpan; -import java.util.Arrays; +import java.text.SimpleDateFormat; +import java.util.Date; import java.util.regex.Matcher; import java.util.regex.Pattern; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Behavior; +import ohi.andre.consolelauncher.tuils.Tuils; /** * Created by francescoandreuzzi on 26/07/2017. @@ -21,28 +22,34 @@ import ohi.andre.consolelauncher.managers.xml.options.Behavior; public class TimeManager { - static String[] formats; - static Time time; + static SimpleDateFormat[] dateFormatList; static Pattern extractor = Pattern.compile("%t([0-9]+)", Pattern.CASE_INSENSITIVE); public static void create() { + final Pattern NEWLINE_PATTERN = Pattern.compile("%n"); + String format = XMLPrefsManager.get(Behavior.time_format); String separator = XMLPrefsManager.get(Behavior.time_format_separator); - time = new Time(); - - formats = format.split(separator); - if(formats.length == 0) formats = new String[] {Tuils.EMPTYSTRING}; + String[] formats = format.split(separator); + dateFormatList = new SimpleDateFormat[formats.length]; - Arrays.asList(formats); + for(int c = 0; c < dateFormatList.length; c++) { + try { + formats[c] = NEWLINE_PATTERN.matcher(formats[c]).replaceAll(Tuils.NEWLINE); + dateFormatList[c] = new SimpleDateFormat(formats[c]); + } catch (Exception e) { + dateFormatList[c] = dateFormatList[0]; + } + } } - public static String get(int index) { - if(formats == null) return null; + private static SimpleDateFormat get(int index) { + if(index < 0 || index >= dateFormatList.length) index = 0; + if(index == 0 && dateFormatList.length == 0) return null; - if(index < 0 || index >= formats.length) index = 0; - return formats[index]; + return dateFormatList[index]; } public static CharSequence replace(CharSequence cs) { @@ -63,18 +70,18 @@ public class TimeManager { public static CharSequence replace(Context context, int size, CharSequence cs, long tm, int color) { if(tm == -1) { - time.setToNow(); - } else { - time.set(tm); + tm = System.currentTimeMillis(); } + Date date = new Date(tm); + Matcher matcher = extractor.matcher(cs.toString()); if(matcher.find()) { for(int count = 1; count <= matcher.groupCount(); count++) { - String t = get(Integer.parseInt(matcher.group(count))); - if(t == null) continue; + SimpleDateFormat formatter = get(Integer.parseInt(matcher.group(count))); + if(formatter == null) return cs; - String tf = time.format(t); + String tf = formatter.format(date); SpannableString spannableString = new SpannableString(tf); if(color != Integer.MAX_VALUE) { @@ -89,10 +96,10 @@ public class TimeManager { } } - String t = get(0); - if(t == null) return cs; + SimpleDateFormat formatter = get(0); + if(formatter == null) return cs; - String tf = time.format(t); + String tf = formatter.format(date); SpannableString spannableString = new SpannableString(tf); if(color != Integer.MAX_VALUE) { diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java index ff93472..95bfdcc 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java @@ -18,6 +18,7 @@ import java.util.List; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Behavior; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; /** @@ -146,7 +147,7 @@ public class MusicManager2 implements MediaController.MediaPlayerControl { } public void updateSongs() { - loader = new Thread() { + loader = new StoppableThread() { @Override public void run() { try { diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/KeeperService.java b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/KeeperService.java new file mode 100755 index 0000000..350a57d --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/KeeperService.java @@ -0,0 +1,220 @@ +package ohi.andre.consolelauncher.managers.notifications; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; +import android.support.v4.app.RemoteInput; +import android.text.SpannableString; +import android.text.TextUtils; + +import ohi.andre.consolelauncher.R; +import ohi.andre.consolelauncher.managers.TimeManager; +import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; +import ohi.andre.consolelauncher.managers.xml.options.Behavior; +import ohi.andre.consolelauncher.managers.xml.options.Theme; +import ohi.andre.consolelauncher.managers.xml.options.Ui; +import ohi.andre.consolelauncher.tuils.InputOutputReceiver; +import ohi.andre.consolelauncher.tuils.Tuils; + +import static ohi.andre.consolelauncher.managers.TerminalManager.FORMAT_INPUT; +import static ohi.andre.consolelauncher.managers.TerminalManager.FORMAT_NEWLINE; +import static ohi.andre.consolelauncher.managers.TerminalManager.FORMAT_PREFIX; + +public class KeeperService extends Service { + +// private final String PATH = "reply.xml"; +// public static final String BIND_NODE = "binding", ID_ATTRIBUTE = "id", APP_ATTRIBUTE = "pkg"; + + public static final int ONGOING_NOTIFICATION_ID = 1001; + public static final String CMD_KEY = "cmd", PATH_KEY = "path"; + + private String title, subtitle, clickCmd, inputFormat, prefix, suPrefix; + private boolean showHome, upDown; + private int inputColor, timeColor, priority; + + private CharSequence[] lastCommands = null; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if(startId == 1 || startId == 0) { + title = XMLPrefsManager.get(Behavior.tui_notification_title); + subtitle = XMLPrefsManager.get(Behavior.tui_notification_subtitle); + clickCmd = XMLPrefsManager.get(Behavior.tui_notification_click_cmd); + inputFormat = XMLPrefsManager.get(Behavior.input_format); + showHome = XMLPrefsManager.getBoolean(Behavior.tui_notification_click_showhome); + inputColor = XMLPrefsManager.getColor(Theme.input_color); + timeColor = XMLPrefsManager.getColor(Theme.time_color); + prefix = XMLPrefsManager.get(Ui.input_prefix); + upDown = XMLPrefsManager.getBoolean(Behavior.tui_notification_lastcmds_updown); + suPrefix = XMLPrefsManager.get(Ui.input_root_prefix); + + priority = XMLPrefsManager.getInt(Behavior.tui_notification_priority); + if(priority > 2) priority = 2; + if(priority < -2) priority = -2; + + String path = intent.getStringExtra(PATH_KEY); + + startForeground(ONGOING_NOTIFICATION_ID, buildNotification(getApplicationContext(), title, subtitle, Tuils.getHint(path), + clickCmd, showHome, lastCommands, upDown, priority)); + + int lastCmdSize = XMLPrefsManager.getInt(Behavior.tui_notification_lastcmds_size); + if(lastCmdSize > 0) { + lastCommands = new CharSequence[lastCmdSize]; + } + + + } else { +// new cmd +// update the list + + if(lastCommands != null) updateCmds(intent.getStringExtra(CMD_KEY)); + + String path = intent.getStringExtra(PATH_KEY); + + NotificationManagerCompat.from(getApplicationContext()).notify(KeeperService.ONGOING_NOTIFICATION_ID, + KeeperService.buildNotification(getApplicationContext(), title, subtitle, Tuils.getHint(path), + clickCmd, showHome, lastCommands, upDown, priority)); + } + + return super.onStartCommand(intent, flags, startId); + } + +// 0 = most recent +// 4 = oldest + +// * = null +// 3 cases +// 1: |*|*|*|*|*| -> lastNull = 0 +// 2: |a|b|c|*|*| -> lastNull = n < length +// 3: |a|b|c|d|e| -> lastNull = -1 + private void updateCmds(String cmd) { + try { + int lastNull = lastNull(); + int toCopy = lastNull == -1 ? lastCommands.length - 1 : lastNull; + System.arraycopy(lastCommands, 0, lastCommands, 1, toCopy); + lastCommands[0] = formatInput(cmd, inputFormat, prefix, suPrefix, inputColor, timeColor); + } catch (Exception e) { + Tuils.log(e); + } + } + + private static CharSequence formatInput(String cmd, String inputFormat, String prefix, String suPrefix, int inputColor, int timeColor) { + if(cmd == null) return null; + boolean su = cmd.startsWith("su "); + + SpannableString si = Tuils.span(inputFormat, inputColor); + + CharSequence s = TimeManager.replace(si, timeColor); + s = TextUtils.replace(s, + new String[] {FORMAT_INPUT, FORMAT_PREFIX, FORMAT_NEWLINE, FORMAT_INPUT.toUpperCase(), FORMAT_PREFIX.toUpperCase(), FORMAT_NEWLINE.toUpperCase()}, + new CharSequence[] {cmd, su ? suPrefix : prefix, Tuils.NEWLINE, cmd, su ? suPrefix : prefix, Tuils.NEWLINE}); + + return s; + } + + private int lastNull() { + for(int c = 0; c < lastCommands.length; c++) if(lastCommands[c] == null) return c; + return -1; + } + + @Override + public boolean onUnbind(Intent intent) { + return true; + } + + private static Notification buildNotification(Context c, String title, String subtitle, String cmdLabel, String clickCmd, boolean showHome, CharSequence[] lastCommands, boolean upDown, int priority) { + + PendingIntent pendingIntent; + if(showHome) { + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if(clickCmd != null && clickCmd.length() > 0) { + startMain.putExtra(InputOutputReceiver.TEXT, clickCmd); + } + + pendingIntent = PendingIntent.getActivity( + c, + 0, + startMain, + PendingIntent.FLAG_CANCEL_CURRENT + ); + } else if(clickCmd != null && clickCmd.length() > 0) { + Intent cmdIntent = new Intent(InputOutputReceiver.ACTION_CMD); + cmdIntent.putExtra(InputOutputReceiver.TEXT, clickCmd); + cmdIntent.putExtra(InputOutputReceiver.SHOW_CONTENT, true); + + pendingIntent = PendingIntent.getBroadcast( + c, + 0, + cmdIntent, + 0 + ); + } else { + pendingIntent = null; + } + + NotificationCompat.Style style = null; + if(lastCommands != null && lastCommands[0] != null) { + NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); + + if(upDown) { + for (CharSequence lastCommand : lastCommands) { + if (lastCommand == null) break; + inboxStyle.addLine(lastCommand); + } + } else { + for(int j = lastCommands.length - 1; j >= 0; j--) { + if(lastCommands[j] == null) continue; + inboxStyle.addLine(lastCommands[j]); + } + } + + style = inboxStyle; + } + + NotificationCompat.Builder builder = new NotificationCompat.Builder(c) + .setSmallIcon(R.mipmap.ic_launcher) + .setTicker(c.getString(R.string.start_notification)) + .setWhen(System.currentTimeMillis()) + .setPriority(priority) + .setContentTitle(title) + .setContentIntent(pendingIntent); + + if(style != null) builder.setStyle(style); + else { + builder.setContentTitle(title); + builder.setContentText(subtitle); + } + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + RemoteInput remoteInput = new RemoteInput.Builder(InputOutputReceiver.TEXT) + .setLabel(cmdLabel) + .build(); + + Intent i = new Intent(InputOutputReceiver.ACTION_CMD); + i.putExtra(InputOutputReceiver.WAS_KEY, InputOutputReceiver.WAS_KEEPER_SERVICE); + + NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, cmdLabel, + PendingIntent.getBroadcast(c.getApplicationContext(), 40, i, PendingIntent.FLAG_UPDATE_CURRENT)) + .addRemoteInput(remoteInput) + .build(); + + builder.addAction(action); + } + + return builder.build(); + } +} diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationManager.java index 7d09624..0ad120e 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationManager.java @@ -2,9 +2,7 @@ package ohi.andre.consolelauncher.managers.notifications; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Color; import android.os.Build; -import android.util.SparseArray; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -14,11 +12,11 @@ import org.w3c.dom.NodeList; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; import java.util.regex.Pattern; -import ohi.andre.consolelauncher.R; +import ohi.andre.consolelauncher.managers.RegexManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Notifications; import ohi.andre.consolelauncher.tuils.Tuils; @@ -26,7 +24,6 @@ import ohi.andre.consolelauncher.tuils.Tuils; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.VALUE_ATTRIBUTE; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.resetFile; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.set; -import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.setMany; import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.writeTo; /** @@ -36,17 +33,7 @@ import static ohi.andre.consolelauncher.managers.xml.XMLPrefsManager.writeTo; @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { - private static int TITLE = 10; - private static int TEXT = 11; - - private static final String COLOR_ATTRIBUTE = "color"; - private static final String ENABLED_ATTRIBUTE = "enabled"; - private static final String ID_ATTRIBUTE = "id"; - private static final String ON_ATTRIBUTE = "on"; - private static final String PACKAGE_ATTRIBUTE = "package"; - - private static final String FILTER_NODE = "filter"; - private static final String APPLY_NODE = "apply"; + private static final String COLOR_ATTRIBUTE = "color", ENABLED_ATTRIBUTE = "enabled", ID_ATTRIBUTE = "id", FORMAT_ATTRIBUTE = "format", FILTER_ATTRIBUTE = "filter"; public static final String PATH = "notifications.xml"; private static final String NAME = "NOTIFICATIONS"; @@ -68,13 +55,13 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { @Override public void write(XMLPrefsManager.XMLPrefsSave save, String value) { - set(new File(Tuils.getFolder(), PATH), NAME, save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); + set(new File(Tuils.getFolder(), PATH), save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); } private static XMLPrefsManager.XMLPrefsList values; private static List<NotificatedApp> apps; - private static List<FilterGroup> groups; - private static SparseArray<List<String>> applies; + private static List<Pattern> filters; + private static List<XMLPrefsManager.IdValue> formats; private NotificationManager() {} @@ -86,8 +73,8 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { instance = new NotificationManager(); apps = new ArrayList<>(); - groups = new ArrayList<>(); - applies = new SparseArray<>(); + filters = new ArrayList<>(); + formats = new ArrayList<>(); values = new XMLPrefsManager.XMLPrefsList(); try { @@ -99,9 +86,12 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { Object[] o; try { o = XMLPrefsManager.buildDocument(file, NAME); + if(o == null) { + Tuils.sendXMLParseError(context, PATH); + return; + } } catch (Exception e) { - Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + PATH + context.getString(R.string.output_xmlproblem2) + - Tuils.NEWLINE + context.getString(R.string.output_errorlabel) + e.toString()); + Tuils.sendXMLParseError(context, PATH, e); return; } @@ -114,7 +104,6 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { String[] deleted = instance.deleted(); boolean needToWrite = false; - Main: for(int count = 0; count < nodes.getLength(); count++) { Node node = nodes.item(count); @@ -128,43 +117,34 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { break; } } - } else if (nn.equals(FILTER_NODE)) { + } else if (nn.equals(FILTER_ATTRIBUTE)) { if (node.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) node; - String regex = e.hasAttribute(VALUE_ATTRIBUTE) ? e.getAttribute(VALUE_ATTRIBUTE) : null; - if (regex == null) continue; - - String on = e.hasAttribute(ON_ATTRIBUTE) ? e.getAttribute(ON_ATTRIBUTE) : null; - if (on == null) on = "text"; + Pattern pattern; - int id; + String regex = XMLPrefsManager.getStringAttribute(e, VALUE_ATTRIBUTE); + if (regex == null) continue; try { - id = e.hasAttribute(ID_ATTRIBUTE) ? Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)) : -1; - } catch (NumberFormatException f) { - id = -1; - } - - Filter filter = Filter.getInstance(regex, on.equals("title") ? TITLE : TEXT); - if (filter == null) continue; - - if (id != -1) { - for (FilterGroup group : groups) { - if (id == group.id) { - group.add(filter); - continue Main; - } + int id = Integer.parseInt(regex); + pattern = RegexManager.get(id).regex; + } catch (Exception exc) { + try { + pattern = Pattern.compile(regex); + } catch (Exception exc2) { + pattern = Pattern.compile(regex, Pattern.LITERAL); } } - FilterGroup group = new FilterGroup(id); - group.add(filter); - groups.add(group); + filters.add(pattern); } - } else if (nn.equals(APPLY_NODE)) { + } else if(nn.equals(FORMAT_ATTRIBUTE)) { if (node.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) node; + String format = XMLPrefsManager.getStringAttribute(e, VALUE_ATTRIBUTE); + if(format == null) continue; + int id; try { id = e.hasAttribute(ID_ATTRIBUTE) ? Integer.parseInt(e.getAttribute(ID_ATTRIBUTE)) : -1; @@ -172,18 +152,9 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { continue; } - String pkg = e.hasAttribute(PACKAGE_ATTRIBUTE) ? e.getAttribute(PACKAGE_ATTRIBUTE) : null; - if (pkg == null) continue; - - List<String> at = applies.get(id); - if(at == null) { - at = new ArrayList<>(); - at.add(pkg); - applies.put(id, at); - } else at.add(pkg); + formats.add(new XMLPrefsManager.IdValue(format, id)); } } else { - int index = deleted == null ? -1 : Tuils.find(nn, deleted); if(index != -1) { deleted[index] = null; @@ -198,13 +169,11 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { NotificatedApp app; - boolean enabled = !e.hasAttribute(ENABLED_ATTRIBUTE) || Boolean.parseBoolean(e.getAttribute(ENABLED_ATTRIBUTE)); - - String color = null; - if (enabled) - color = e.hasAttribute(COLOR_ATTRIBUTE) ? e.getAttribute(COLOR_ATTRIBUTE) : null; + boolean enabled = XMLPrefsManager.getBooleanAttribute(e, ENABLED_ATTRIBUTE); + String color = XMLPrefsManager.getStringAttribute(e, COLOR_ATTRIBUTE); + String format = XMLPrefsManager.getStringAttribute(e, FORMAT_ATTRIBUTE); - app = new NotificatedApp(nn, color, enabled); + app = new NotificatedApp(nn, color, format, enabled); apps.add(app); } } @@ -230,70 +199,60 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { Tuils.toFile(e); } + for(NotificatedApp app : apps) { + try { + int formatID = Integer.parseInt(app.format); + + for(XMLPrefsManager.IdValue idValue : formats) { + if(idValue.id == formatID) { + app.format = idValue.value; + break; + } + } + } catch (Exception e) {} + } + default_app_state = XMLPrefsManager.getBoolean(Notifications.app_notification_enabled_default); default_color = XMLPrefsManager.get(Notifications.default_notification_color); + } - Out: - for(int count = 0; count < applies.size(); count++) { - int id = applies.keyAt(count); - List<String> pkgs = applies.get(id); - - for(FilterGroup g : groups) { - if(g.applyTo(id, pkgs)) continue Out; - } - } + public static String setState(String pkg, boolean state) { + return XMLPrefsManager.set(new File(Tuils.getFolder(), PATH), pkg, new String[] {ENABLED_ATTRIBUTE}, new String[] {String.valueOf(state)}); } - public static void notificationsChangeFor(NotificatedApp app) { - notificationsChangeFor(new ArrayList<>(Collections.singletonList(app))); + public static String setColor(String pkg, String color) { + return XMLPrefsManager.set(new File(Tuils.getFolder(), PATH), pkg, new String[] {ENABLED_ATTRIBUTE, COLOR_ATTRIBUTE}, new String[] {String.valueOf(true), color}); } - public static boolean match(String pkg, String text, String title) { -// if(pkg.equals(BuildConfig.APPLICATION_ID)) return true; + public static String setFormat(String pkg, String format) { + return XMLPrefsManager.set(new File(Tuils.getFolder(), PATH), pkg, new String[] {FORMAT_ATTRIBUTE}, new String[] {format}); + } - for(FilterGroup group : groups) { + public static String addFilter(String pattern, int id) { + return XMLPrefsManager.add(new File(Tuils.getFolder(), PATH), FILTER_ATTRIBUTE, new String[] {ID_ATTRIBUTE, VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), pattern}); + } - if(group.pkgs != null && !group.pkgs.contains(pkg)) { - continue; - } + public static String addFormat(String format, int id) { + return XMLPrefsManager.add(new File(Tuils.getFolder(), PATH), FORMAT_ATTRIBUTE, new String[] {ID_ATTRIBUTE, VALUE_ATTRIBUTE}, new String[] {String.valueOf(id), format}); + } - if(group.check(title, text)) { - return true; - } - } - return false; + public static String rmFilter(int id) { + return XMLPrefsManager.removeNode(new File(Tuils.getFolder(), PATH), FILTER_ATTRIBUTE, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}); } - public static String getFormat() { - try { - return values.get(Notifications.notification_format).value; - } catch (Exception e) { - Tuils.toFile(e); - return Notifications.notification_format.defaultValue(); - } + public static String rmFormat(int id) { + return XMLPrefsManager.removeNode(new File(Tuils.getFolder(), PATH), FORMAT_ATTRIBUTE, new String[] {ID_ATTRIBUTE}, new String[] {String.valueOf(id)}); } - public static void notificationsChangeFor(List<NotificatedApp> apps) { - String[] names = new String[apps.size()]; - final String[] attrNames = {ENABLED_ATTRIBUTE, COLOR_ATTRIBUTE}; - String[][] values = new String[names.length][attrNames.length]; + public static boolean match(String pkg, String text) { +// if(pkg.equals(BuildConfig.APPLICATION_ID)) return true; - for(int count = 0; count < apps.size(); count++) { - NotificatedApp app = apps.get(count); - names[count] = app.pkg; - values[count][0] = app.enabled + Tuils.EMPTYSTRING; - values[count][1] = app.color != null ? app.color : Tuils.EMPTYSTRING; + for(Pattern f : filters) { + Matcher m = f.matcher(text); + if(m.matches() || m.find()) return true; } - setMany(new File(Tuils.getFolder(), PATH), NAME, names, attrNames, values); - } - - public static void excludeRegex(String regex, String on, int id) { - XMLPrefsManager.add(new File(Tuils.getFolder(), PATH), NAME, FILTER_NODE, new String[] {ON_ATTRIBUTE, VALUE_ATTRIBUTE, ID_ATTRIBUTE}, new String[] {on, regex, id + Tuils.EMPTYSTRING}); - } - - public static void applyFilter(int groupId, String packageName) { - XMLPrefsManager.add(new File(Tuils.getFolder(), PATH), NAME, APPLY_NODE, new String[] {ID_ATTRIBUTE, PACKAGE_ATTRIBUTE}, new String[] {groupId + Tuils.EMPTYSTRING, packageName}); + return false; } public static int apps() { @@ -308,14 +267,14 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { } public static class NotificatedApp { - String pkg; - String color; + String pkg, color, format; boolean enabled; - public NotificatedApp(String pkg, String color, boolean enabled) { + public NotificatedApp(String pkg, String color, String format, boolean enabled) { this.pkg = pkg; this.color = color; this.enabled = enabled; + this.format = format; } @Override @@ -328,92 +287,4 @@ public class NotificationManager implements XMLPrefsManager.XmlPrefsElement { return pkg; } } - - public static class Filter { - Pattern pattern; - int on; - - public static Filter getInstance(String p, int on) { - Filter filter = new Filter(p, on); - if(filter.pattern == null) return null; - return filter; - } - - private Filter(String p, int on) { - this.on = on; - - try { - this.pattern = Pattern.compile(p); - } catch (Exception e) { - this.pattern = null; - } - } - } - - public static class FilterGroup { - List<Filter> brothers; - int id; - - List<String> pkgs; - - public FilterGroup(int id) { - this.id = id; - - brothers = new ArrayList<>(); - } - - public void add(Filter filter) { - brothers.add(filter); - } - - public boolean check(String title, String text) { - boolean matchTitle = false, matchText = false; - int titleCount = 0, textCount = 0; - - for(Filter filter : brothers) { - String s; - if(filter.on == TITLE) { - s = title; - titleCount++; - } else { - s = text; - textCount++; - } - - if(s == null) continue; - - boolean b = filter.pattern.matcher(s).find(); - - if(filter.on == TITLE) matchTitle = matchTitle || b; - else matchText = matchText || b; - } - - matchTitle = matchTitle || titleCount == 0; - matchText = matchText || textCount == 0; - - return matchText && matchTitle; - } - - public boolean applyTo(int id, String s) { - if(this.id == id) { - if(pkgs == null) pkgs = new ArrayList<>(); - - pkgs.add(s); - return true; - } - - return false; - } - - public boolean applyTo(int id, List<String> ss) { - if(this.id == id) { - if(pkgs == null) pkgs = new ArrayList<>(); - - pkgs.addAll(ss); - return true; - } - - return false; - } - } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationService.java b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationService.java index ec316ba..b4d60a3 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationService.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/notifications/NotificationService.java @@ -5,84 +5,275 @@ package ohi.andre.consolelauncher.managers.notifications; */ import android.annotation.TargetApi; -import android.app.Notification; +import android.app.PendingIntent; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.support.v4.app.NotificationCompat; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.util.SparseArray; +import android.text.TextUtils; import java.util.ArrayList; -import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; import java.util.regex.Matcher; import java.util.regex.Pattern; import ohi.andre.consolelauncher.managers.TerminalManager; +import ohi.andre.consolelauncher.managers.TimeManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; +import ohi.andre.consolelauncher.managers.xml.options.Behavior; import ohi.andre.consolelauncher.managers.xml.options.Notifications; import ohi.andre.consolelauncher.managers.xml.options.Theme; -import ohi.andre.consolelauncher.tuils.TimeManager; +import ohi.andre.consolelauncher.tuils.StoppableThread; import ohi.andre.consolelauncher.tuils.Tuils; -import static ohi.andre.consolelauncher.managers.notifications.NotificationManager.NotificatedApp; -import static ohi.andre.consolelauncher.managers.notifications.NotificationManager.default_color; - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public class NotificationService extends NotificationListenerService { - private final int UPDATE_TIME = 1500; + private final int UPDATE_TIME = 2000; + private final String LINES_LABEL = "Lines", ANDROID_LABEL_PREFIX = "android.", NULL_LABEL = "null"; - SparseArray<Long> ids = new SparseArray<>(); + HashMap<String, List<Notification>> pastNotifications; Handler handler = new Handler(); + String format; + int timeColor, color, maxOptionalDepth; + boolean enabled, click, longClick; + + Queue<StatusBarNotification> queue; + + final String PKG = "%pkg", APP = "%app", NEWLINE = "%n"; + final Pattern timePattern = Pattern.compile("^%t[0-9]*$"); + + PackageManager manager; + + private final Pattern formatPattern = Pattern.compile("%(?:\\[(\\d+)\\])?(?:\\[([^]]+)\\])?(?:(?:\\{)([a-zA-Z\\.\\:\\s]+)(?:\\})|([a-zA-Z\\.\\:]+))"); + + StoppableThread bgThread = new StoppableThread() { + @Override + public void run() { + super.run(); + + if(!enabled) return; + + while(true) { + if(queue != null) { + + StatusBarNotification sbn; + while ((sbn = queue.poll()) != null) { + + android.app.Notification notification = sbn.getNotification(); + if (notification == null) { + continue; + } + + String pack = sbn.getPackageName(); + + String appName; + try { + appName = manager.getApplicationInfo(pack, 0).loadLabel(manager).toString(); + } catch (PackageManager.NameNotFoundException e) { + appName = "null"; + } + + NotificationManager.NotificatedApp nApp = NotificationManager.getAppState(pack); + if ((nApp != null && !nApp.enabled)) { + continue; + } + + if (nApp == null && !NotificationManager.default_app_state) { + continue; + } + + String f; + if(nApp != null && nApp.format != null) f = nApp.format; + else f = format; + + int textColor; + if(nApp != null && nApp.color != null) textColor = Color.parseColor(nApp.color); + else textColor = color; + + CharSequence s = Tuils.span(f, textColor); + + Bundle bundle = NotificationCompat.getExtras(notification); + + if(bundle != null) { + Matcher m = formatPattern.matcher(s); + String match; + while(m.find()) { + match = m.group(0); + if (!match.startsWith(PKG) && !match.startsWith(APP) && !match.startsWith(NEWLINE) && !timePattern.matcher(match).matches()) { + String length = m.group(1); + String color = m.group(2); + String value = m.group(3); + + if(value == null || value.length() == 0) value = m.group(4); + + if(value != null) value = value.trim(); + else continue; + + if(value.length() == 0) continue; + + if(value.equals("ttl")) value = "title"; + else if(value.equals("txt")) value = "text"; + + String[] temp = value.split(":"), split; + if(value.endsWith(":")) { + split = new String[temp.length + 1]; + System.arraycopy(temp, 0, split, 0, temp.length); + split[split.length - 1] = Tuils.EMPTYSTRING; + } else split = temp; + +// because the last one is the default text, but only if there is more than one label + int stopAt = split.length; + if(stopAt > 1) stopAt--; + + CharSequence text = null; + for(int j = 0; j < stopAt; j++) { + if(split[j].contains(LINES_LABEL)) { + CharSequence[] array = bundle.getCharSequenceArray(ANDROID_LABEL_PREFIX + split[j]); + if(array != null) { + for(CharSequence c : array) { + if(text == null) text = c; + else text = TextUtils.concat(text, Tuils.NEWLINE, c); + } + } + } else { + text = bundle.getCharSequence(ANDROID_LABEL_PREFIX + split[j]); + } + + if(text != null && text.length() > 0) break; + } + + if(text == null || text.length() == 0) { + text = split.length == 1 ? NULL_LABEL : split[split.length - 1]; + } + + String stringed = text.toString().trim(); + + try { + int l = Integer.parseInt(length); + stringed = stringed.substring(0,l); + } catch (Exception e) {} + + try { + text = Tuils.span(stringed, Color.parseColor(color)); + } catch (Exception e) { + text = stringed; + } + + s = TextUtils.replace(s, new String[] {m.group(0)}, new CharSequence[] {text}); + } + } + } + + String text = s.toString(); + + if(NotificationManager.match(pack, text)) continue; + + int found = isInPastNotifications(pack, text); +// if(found == 0) { +// Tuils.log("app " + pack, pastNotifications.get(pack).toString()); +// } + + if(found == 2) continue; + +// else + Notification n = new Notification(System.currentTimeMillis(), text, pack, notification.contentIntent); + + if(found == 1) { + List<Notification> ns = new ArrayList<>(); + ns.add(n); + pastNotifications.put(pack, ns); + } else if(found == 0) { + pastNotifications.get(pack).add(n); + } + + s = TextUtils.replace(s, new String[]{PKG, APP, NEWLINE}, new CharSequence[]{pack, appName, Tuils.NEWLINE}); + String st = s.toString(); + while (st.contains(NEWLINE)) { + s = TextUtils.replace(s, + new String[]{NEWLINE}, + new CharSequence[]{Tuils.NEWLINE}); + st = s.toString(); + } + + try { + s = TimeManager.replace(s, timeColor); + } catch (Exception e) { + Tuils.log(e); + } + + Tuils.sendOutput(NotificationService.this.getApplicationContext(), s, TerminalManager.CATEGORY_NOTIFICATION, click ? notification.contentIntent : null, longClick ? n : null); + } + } + + try { + sleep(UPDATE_TIME); + } catch (InterruptedException e) { + Tuils.log(e); + return; + } + } + } + }; + @Override public void onCreate() { super.onCreate(); NotificationManager.create(this); + try { + XMLPrefsManager.create(this); + } catch (Exception e) { + return; + } manager = getPackageManager(); - format = NotificationManager.getFormat(); - enabled = XMLPrefsManager.getBoolean(Notifications.show_notifications) || - XMLPrefsManager.get(Notifications.show_notifications).equalsIgnoreCase("enabled"); - - if(NotificationManager.apps() == 0) { - NotificationManager.notificationsChangeFor(new ArrayList<>(Arrays.asList( - new NotificatedApp("com.whatsapp", "#25D366", true), - new NotificatedApp("com.google.android.apps.inbox", "#03A9F4", true), - new NotificatedApp("com.paypal.android.p2pmobile", "#003087", true), - new NotificatedApp("com.google.android.apps.plus", "#dd4b39", true), - new NotificatedApp("com.facebook.katana", "#3b5998", true), - new NotificatedApp("com.twitter.android", "#1da1f2", true), - new NotificatedApp("com.android.vending", "#34a853", true) - ))); - } + enabled = XMLPrefsManager.getBoolean(Notifications.show_notifications) || XMLPrefsManager.get(Notifications.show_notifications).equalsIgnoreCase("enabled"); + + pastNotifications = new HashMap<>(); + + format = XMLPrefsManager.get(Notifications.notification_format); + color = XMLPrefsManager.getColor(Notifications.default_notification_color); + + click = XMLPrefsManager.getBoolean(Notifications.click_notification); + longClick = XMLPrefsManager.getBoolean(Notifications.long_click_notification); + + maxOptionalDepth = XMLPrefsManager.getInt(Behavior.max_optional_depth); handler.post(new Runnable() { @Override public void run() { - SparseArray<Long> clone = ids.clone(); + long now = System.currentTimeMillis(); - long time = System.currentTimeMillis(); - for(int c = 0; c < clone.size(); c++) { - int key = clone.keyAt(c); - long tm = clone.valueAt(c); + for (Map.Entry<String, List<Notification>> entry : pastNotifications.entrySet()) { + List<Notification> notifications = entry.getValue(); - if(time - tm > UPDATE_TIME) ids.remove(key); + Iterator<Notification> it = notifications.iterator(); + while (it.hasNext()) { + if (now - it.next().time >= UPDATE_TIME) it.remove(); + } } handler.postDelayed(this, UPDATE_TIME); } }); + + queue = new ArrayBlockingQueue<>(5); + bgThread.start(); } @Override @@ -99,113 +290,70 @@ public class NotificationService extends NotificationListenerService { @Override public void onDestroy() { super.onDestroy(); - handler.removeCallbacksAndMessages(null); + if(handler != null) handler.removeCallbacksAndMessages(null); } - String format; - int timeColor; - boolean enabled; - - final Pattern patternPkg = Pattern.compile("%pkg", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - final Pattern patternText = Pattern.compile("%txt", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - final Pattern patternTitle = Pattern.compile("%ttl", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - final Pattern patternAppname = Pattern.compile("%app", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - final Pattern patternNewline = Pattern.compile("%n", Pattern.CASE_INSENSITIVE | Pattern.LITERAL); - - PackageManager manager; - @Override public void onNotificationPosted(StatusBarNotification sbn) { - if(!enabled) return; + queue.offer(sbn); + } - for(int c = 0; c < ids.size(); c++) { - int key = ids.keyAt(c); - if(key == sbn.getId()) return; - } - ids.put(sbn.getId(), System.currentTimeMillis()); - - Notification notification = sbn.getNotification(); - if (notification == null) { - return; - } +// 0 = not found +// 1 = the app wasnt found -> this is the first notification from this app +// 2 = found + private int isInPastNotifications(String pkg, String text) { + List<Notification> notifications = pastNotifications.get(pkg); + if(notifications == null) return 1; + for(Notification n : notifications) if(n.text.equals(text)) return 2; + return 0; + } - String pack = sbn.getPackageName(); + @Override + public void onNotificationRemoved(StatusBarNotification sbn) {} - String appName; - try { - appName = manager.getApplicationInfo(pack, 0).loadLabel(manager).toString(); - } catch (PackageManager.NameNotFoundException e) { - appName = "null"; - } + public static class Notification implements Parcelable { + public long time; + public String text, pkg; + public PendingIntent pendingIntent; - NotificatedApp nApp = NotificationManager.getAppState(pack); - if ((nApp != null && !nApp.enabled)) { - return; + public Notification(long time, String text, String pkg, PendingIntent pi) { + this.time = time; + this.text = text; + this.pkg = pkg; + this.pendingIntent = pi; } - if (nApp == null && !NotificationManager.default_app_state) { - return; + protected Notification(Parcel in) { + time = in.readLong(); + text = in.readString(); + pkg = in.readString(); + pendingIntent = in.readParcelable(PendingIntent.class.getClassLoader()); } - CharSequence textSequence = null, titleSequence = null; - - Bundle bundle = NotificationCompat.getExtras(notification); - if(bundle != null) { - textSequence = bundle.getCharSequence(NotificationCompat.EXTRA_TEXT); - titleSequence = bundle.getCharSequence(NotificationCompat.EXTRA_TITLE); - - if (textSequence == null) { - CharSequence[] charText = (CharSequence[]) bundle.get(NotificationCompat.EXTRA_TEXT_LINES); - if (charText != null && charText.length > 0) { - textSequence = charText[charText.length - 1].toString(); - } + public static final Creator<Notification> CREATOR = new Creator<Notification>() { + @Override + public Notification createFromParcel(Parcel in) { + return new Notification(in); } - } else { - textSequence = notification.tickerText; - } - String text = null, title = null; - if(textSequence != null) { - text = textSequence.toString(); - } - - if(titleSequence != null) { - title = titleSequence.toString(); - } - - if(title == null) title = "null"; - if(text == null) text = "null"; - - if(NotificationManager.match(pack, text, title)) return; + @Override + public Notification[] newArray(int size) { + return new Notification[size]; + } + }; - int color; - try { - color = Color.parseColor(nApp.color); - } catch (Exception e) { - color = Color.parseColor(default_color); + @Override + public int describeContents() { + return 0; } - String finalText = format; - finalText = patternPkg.matcher(finalText).replaceAll(Matcher.quoteReplacement(pack)); - finalText = patternAppname.matcher(finalText).replaceAll(Matcher.quoteReplacement(appName)); - finalText = patternText.matcher(finalText).replaceAll(Matcher.quoteReplacement(text)); - finalText = patternTitle.matcher(finalText).replaceAll(Matcher.quoteReplacement(title)); - finalText = patternNewline.matcher(finalText).replaceAll(Matcher.quoteReplacement(Tuils.NEWLINE)); - - SpannableString spannableString = new SpannableString(finalText); - spannableString.setSpan(new ForegroundColorSpan(color), 0, finalText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - CharSequence s; - try { - s = TimeManager.replace(spannableString, timeColor); - } catch (Exception e) { - return; + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(time); + dest.writeString(text); + dest.writeString(pkg); + dest.writeParcelable(pendingIntent, flags); } - - Tuils.sendOutput(this, s, TerminalManager.CATEGORY_NOTIFICATION); } - - @Override - public void onNotificationRemoved(StatusBarNotification sbn) {} } \ No newline at end of file diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java index 6729e7a..38b2450 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java @@ -17,11 +17,13 @@ import ohi.andre.consolelauncher.managers.AliasManager; import ohi.andre.consolelauncher.managers.AppsManager; import ohi.andre.consolelauncher.managers.ContactManager; import ohi.andre.consolelauncher.managers.FileManager; +import ohi.andre.consolelauncher.managers.RssManager; import ohi.andre.consolelauncher.managers.music.Song; import ohi.andre.consolelauncher.managers.notifications.NotificationManager; import ohi.andre.consolelauncher.managers.xml.XMLPrefsManager; import ohi.andre.consolelauncher.managers.xml.options.Apps; import ohi.andre.consolelauncher.managers.xml.options.Notifications; +import ohi.andre.consolelauncher.managers.xml.options.Rss; import ohi.andre.consolelauncher.managers.xml.options.Suggestions; import ohi.andre.consolelauncher.tuils.Compare; import ohi.andre.consolelauncher.tuils.SimpleMutableEntry; @@ -44,7 +46,7 @@ public class SuggestionsManager { private boolean showAliasDefault, set = false, clickToLaunch, showAppsGpDefault; - public Suggestion[] getSuggestions(MainPack info, String before, String lastWord) { + public Suggestion[] getSuggestions(MainPack info, String beforeLastSpace , String lastWord) { if(!set) { showAliasDefault = XMLPrefsManager.getBoolean(Suggestions.suggest_alias_default); @@ -55,14 +57,14 @@ public class SuggestionsManager { List<Suggestion> suggestionList = new ArrayList<>(); - before = before.trim(); + beforeLastSpace = beforeLastSpace .trim(); lastWord = lastWord.trim(); // lastword = 0 if (lastWord.length() == 0) { -// lastword = 0 && before = 0 +// lastword = 0 && beforeLastSpace = 0 - if (before.length() == 0) { + if (beforeLastSpace .length() == 0) { AppsManager.LaunchInfo[] apps = info.appsManager.getSuggestedApps(); if (apps != null) { for(int count = 0; count < apps.length; count++) { @@ -72,22 +74,22 @@ public class SuggestionsManager { float shift = count + 1; float rate = 1f / shift; - suggestionList.add(new Suggestion(before, apps[count].getString(), clickToLaunch, (int) Math.ceil(rate), Suggestion.TYPE_APP, apps[count])); + suggestionList.add(new Suggestion(beforeLastSpace , apps[count].getString(), clickToLaunch, (int) Math.ceil(rate), Suggestion.TYPE_APP, apps[count])); } } if(showAliasDefault) suggestAlias(info.aliasManager, suggestionList, lastWord); - if(showAppsGpDefault) suggestAppGroup(suggestionList, lastWord, before); + if(showAppsGpDefault) suggestAppGroup(suggestionList, lastWord, beforeLastSpace ); return suggestionList.toArray(new Suggestion[suggestionList.size()]); } -// lastword == 0 && before > 0 +// lastword == 0 && beforeLastSpace > 0 else { // check if this is a command Command cmd = null; try { - cmd = CommandTuils.parse(before, info, true); + cmd = CommandTuils.parse(beforeLastSpace , info, true); } catch (Exception e) {} if (cmd != null) { @@ -105,17 +107,17 @@ public class SuggestionsManager { // (cmd.mArgs != null && cmd.mArgs.length > 0 && cmd.cmd instanceof ParamCommand && cmd.nArgs >= 1 && ((ParamCommand) cmd.cmd).argsForParam((String) cmd.mArgs[0]).length == cmd.nArgs // && cmd.indexNotFound == ((ParamCommand) cmd.cmd).argsForParam((String) cmd.mArgs[0]).length)) { //// the last arg wasnt found -// suggestArgs(info, cmd.cmd instanceof ParamCommand ? ((ParamCommand) cmd.cmd).argsForParam((String) cmd.mArgs[0])[cmd.nArgs - 1] : cmd.cmd.argType()[cmd.nArgs], suggestionList, lastWord, before); +// suggestArgs(info, cmd.cmd instanceof ParamCommand ? ((ParamCommand) cmd.cmd).argsForParam((String) cmd.mArgs[0])[cmd.nArgs - 1] : cmd.cmd.argType()[cmd.nArgs], suggestionList, lastWord, beforeLastSpace ); // } if(cmd.cmd instanceof ParamCommand && (cmd.mArgs == null || cmd.mArgs.length == 0 || cmd.mArgs[0] instanceof String)) { - suggestParams(info, suggestionList, (ParamCommand) cmd.cmd, before, null); + suggestParams(info, suggestionList, (ParamCommand) cmd.cmd, beforeLastSpace , null); } - else suggestArgs(info, cmd.nextArg(), suggestionList, before); + else suggestArgs(info, cmd.nextArg(), suggestionList, beforeLastSpace ); } else { - String[] split = before.replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE); + String[] split = beforeLastSpace .replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE); boolean isShellCmd = false; for(String s : split) { if(needsFileSuggestion(s)) { @@ -125,10 +127,10 @@ public class SuggestionsManager { } if(isShellCmd) { - suggestFile(info, suggestionList, Tuils.EMPTYSTRING, before); + suggestFile(info, suggestionList, Tuils.EMPTYSTRING, beforeLastSpace ); } else { // ==> app - if(!suggestAppInsideGroup(suggestionList, Tuils.EMPTYSTRING, before, false)) suggestApp(info, suggestionList, before + Tuils.SPACE, Tuils.EMPTYSTRING); + if(!suggestAppInsideGroup(suggestionList, Tuils.EMPTYSTRING, beforeLastSpace , false)) suggestApp(info, suggestionList, beforeLastSpace + Tuils.SPACE, Tuils.EMPTYSTRING); } } @@ -138,11 +140,11 @@ public class SuggestionsManager { // lastWord > 0 else { - if (before.length() > 0) { -// lastword > 0 && before > 0 + if (beforeLastSpace .length() > 0) { +// lastword > 0 && beforeLastSpace > 0 Command cmd = null; try { - cmd = CommandTuils.parse(before, info, true); + cmd = CommandTuils.parse(beforeLastSpace , info, true); } catch (Exception e) {} if (cmd != null) { @@ -150,18 +152,18 @@ public class SuggestionsManager { suggestPermanentSuggestions(suggestionList, (PermanentSuggestionCommand) cmd.cmd); } -// if (cmd.cmd.maxArgs() == 1 && before.contains(Tuils.SPACE)) { +// if (cmd.cmd.maxArgs() == 1 && beforeLastSpace .contains(Tuils.SPACE)) { // int index = cmd.cmd.getClass().getSimpleName().length() + 1; // -// lastWord = before.substring(index) + lastWord; +// lastWord = beforeLastSpace .substring(index) + lastWord; // } if(cmd.cmd instanceof ParamCommand && (cmd.mArgs == null || cmd.mArgs.length == 0 || cmd.mArgs[0] instanceof String)) { - suggestParams(info, suggestionList, (ParamCommand) cmd.cmd, before, lastWord); - } else suggestArgs(info, cmd.nextArg(), suggestionList, lastWord, before); + suggestParams(info, suggestionList, (ParamCommand) cmd.cmd, beforeLastSpace , lastWord); + } else suggestArgs(info, cmd.nextArg(), suggestionList, lastWord, beforeLastSpace ); } else { - String[] split = before.replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE); + String[] split = beforeLastSpace .replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE); boolean isShellCmd = false; for(String s : split) { if(needsFileSuggestion(s)) { @@ -171,18 +173,18 @@ public class SuggestionsManager { } if(isShellCmd) { - suggestFile(info, suggestionList, lastWord, before); + suggestFile(info, suggestionList, lastWord, beforeLastSpace ); } else { - if(!suggestAppInsideGroup(suggestionList, lastWord, before, false)) suggestApp(info, suggestionList, before + Tuils.SPACE + lastWord, Tuils.EMPTYSTRING); + if(!suggestAppInsideGroup(suggestionList, lastWord, beforeLastSpace , false)) suggestApp(info, suggestionList, beforeLastSpace + Tuils.SPACE + lastWord, Tuils.EMPTYSTRING); } } } else { -// lastword > 0 && before = 0 - suggestCommand(info, suggestionList, lastWord, before); +// lastword > 0 && beforeLastSpace = 0 + suggestCommand(info, suggestionList, lastWord, beforeLastSpace ); suggestAlias(info.aliasManager, suggestionList, lastWord); suggestApp(info, suggestionList, lastWord, Tuils.EMPTYSTRING); - suggestAppGroup(suggestionList, lastWord, before); + suggestAppGroup(suggestionList, lastWord, beforeLastSpace ); } } @@ -207,7 +209,7 @@ public class SuggestionsManager { else for(String s : aliasManager.getAliases()) if(s.startsWith(lastWord)) suggestions.add(new Suggestion(Tuils.EMPTYSTRING, s, clickToLaunch, NO_RATE, Suggestion.TYPE_ALIAS)); } - private void suggestParams(MainPack pack, List<Suggestion> suggestions, ParamCommand cmd, String before, String lastWord) { + private void suggestParams(MainPack pack, List<Suggestion> suggestions, ParamCommand cmd, String beforeLastSpace , String lastWord) { String[] params = cmd.params(); if (params == null) { return; @@ -218,7 +220,7 @@ public class SuggestionsManager { Param p = cmd.getParam(pack, s).getValue(); if(p == null) continue; - suggestions.add(new Suggestion(before, s, p.args().length == 0 && clickToLaunch, NO_RATE, 0)); + suggestions.add(new Suggestion(beforeLastSpace , s, p.args().length == 0 && clickToLaunch, NO_RATE, 0)); } } else { @@ -227,108 +229,108 @@ public class SuggestionsManager { if(p == null) continue; if (s.startsWith(lastWord) || s.replace("-", Tuils.EMPTYSTRING).startsWith(lastWord)) { - suggestions.add(new Suggestion(before, s, p.args().length == 0 && clickToLaunch, NO_RATE, 0)); + suggestions.add(new Suggestion(beforeLastSpace , s, p.args().length == 0 && clickToLaunch, NO_RATE, 0)); } } } } - private void suggestArgs(MainPack info, int type, List<Suggestion> suggestions, String prev, String before) { + private void suggestArgs(MainPack info, int type, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { switch (type) { case CommandAbstraction.FILE: case CommandAbstraction.FILE_LIST: - suggestFile(info, suggestions, prev, before); + suggestFile(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.VISIBLE_PACKAGE: - suggestApp(info, suggestions, prev, before); + suggestApp(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.COMMAND: - suggestCommand(info, suggestions, prev, before); + suggestCommand(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.CONTACTNUMBER: - suggestContact(info, suggestions, prev, before); + suggestContact(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.SONG: - suggestSong(info, suggestions, prev, before); + suggestSong(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.BOOLEAN: - suggestBoolean(suggestions, before); + suggestBoolean(suggestions, beforeLastSpace ); break; case CommandAbstraction.HIDDEN_PACKAGE: - suggestHiddenApp(info, suggestions, prev, before); + suggestHiddenApp(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.COLOR: - suggestColor(suggestions, prev, before); + suggestColor(suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.CONFIG_ENTRY: - suggestConfigEntry(suggestions, prev, before); + suggestConfigEntry(suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.CONFIG_FILE: - suggestConfigFile(suggestions, prev, before); + suggestConfigFile(suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.DEFAULT_APP: - suggestDefaultApp(info, suggestions, prev, before); + suggestDefaultApp(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.ALL_PACKAGES: - suggestAllPackages(info, suggestions, prev, before); + suggestAllPackages(info, suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.APP_GROUP: - suggestAppGroup(suggestions, prev, before); + suggestAppGroup(suggestions, afterLastSpace, beforeLastSpace ); break; case CommandAbstraction.APP_INSIDE_GROUP: - suggestAppInsideGroup(suggestions, prev, before, true); + suggestAppInsideGroup(suggestions, afterLastSpace, beforeLastSpace , true); break; } } - private void suggestArgs(MainPack info, int type, List<Suggestion> suggestions, String before) { - suggestArgs(info, type, suggestions, null, before); + private void suggestArgs(MainPack info, int type, List<Suggestion> suggestions, String beforeLastSpace ) { + suggestArgs(info, type, suggestions, null, beforeLastSpace ); } - private void suggestBoolean(List<Suggestion> suggestions, String before) { - suggestions.add(new Suggestion(before, "true", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN)); - suggestions.add(new Suggestion(before, "false", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN)); + private void suggestBoolean(List<Suggestion> suggestions, String beforeLastSpace ) { + suggestions.add(new Suggestion(beforeLastSpace , "true", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN)); + suggestions.add(new Suggestion(beforeLastSpace , "false", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN)); } - private void suggestFile(MainPack info, List<Suggestion> suggestions, String prev, String before) { - if(prev == null || !prev.endsWith(File.separator)) { - suggestions.add(new Suggestion(before + Tuils.SPACE + (prev != null ? prev : Tuils.EMPTYSTRING), File.separator, false, MAX_RATE, Suggestion.TYPE_FILE)); + private void suggestFile(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { + if(afterLastSpace == null || !afterLastSpace.endsWith(File.separator)) { + suggestions.add(new Suggestion(beforeLastSpace + Tuils.SPACE + (afterLastSpace != null ? afterLastSpace : Tuils.EMPTYSTRING), File.separator, false, MAX_RATE, Suggestion.TYPE_FILE)); } - if (prev == null || prev.length() == 0) { - suggestFilesInDir(suggestions, info.currentDirectory, before); + if (afterLastSpace == null || afterLastSpace.length() == 0) { + suggestFilesInDir(suggestions, info.currentDirectory, beforeLastSpace ); return; } - if (!prev.contains(File.separator) && prev.length() > 0) { - suggestFilesInDir(suggestions, info.currentDirectory, prev, before); - } else if (prev.length() > 0) { - if (prev.endsWith(File.separator)) { - prev = prev.substring(0, prev.length() - 1); - before = before + Tuils.SPACE + prev + File.separator; + if (!afterLastSpace.contains(File.separator) && afterLastSpace.length() > 0) { + suggestFilesInDir(suggestions, info.currentDirectory, afterLastSpace, beforeLastSpace ); + } else if (afterLastSpace.length() > 0) { + if (afterLastSpace.endsWith(File.separator)) { + afterLastSpace = afterLastSpace.substring(0, afterLastSpace.length() - 1); + beforeLastSpace = beforeLastSpace + Tuils.SPACE + afterLastSpace + File.separator; - FileManager.DirInfo dirInfo = FileManager.cd(info.currentDirectory, prev); - suggestFilesInDir(suggestions, dirInfo.file, before); + FileManager.DirInfo dirInfo = FileManager.cd(info.currentDirectory, afterLastSpace); + suggestFilesInDir(suggestions, dirInfo.file, beforeLastSpace ); } else { // contains / but doesn't end with it - FileManager.DirInfo dirInfo = FileManager.cd(info.currentDirectory, prev.substring(0,prev.lastIndexOf(File.separator))); + FileManager.DirInfo dirInfo = FileManager.cd(info.currentDirectory, afterLastSpace.substring(0,afterLastSpace.lastIndexOf(File.separator))); - int index = prev.lastIndexOf(File.separator); - String hold = prev.substring(0, index + 1); - prev = prev.substring(index + 1); - before = before + Tuils.SPACE + hold; + int index = afterLastSpace.lastIndexOf(File.separator); + String hold = afterLastSpace.substring(0, index + 1); + afterLastSpace = afterLastSpace.substring(index + 1); + beforeLastSpace = beforeLastSpace + Tuils.SPACE + hold; - suggestFilesInDir(suggestions, dirInfo.file, prev, before); + suggestFilesInDir(suggestions, dirInfo.file, afterLastSpace, beforeLastSpace ); } } } - private void suggestFilesInDir(List<Suggestion> suggestions, File dir, String prev, String before) { + private void suggestFilesInDir(List<Suggestion> suggestions, File dir, String afterLastSpace, String beforeLastSpace ) { if (dir == null || !dir.isDirectory()) return; - if (prev == null || prev.length() == 0) { - suggestFilesInDir(suggestions, dir, before); + if (afterLastSpace == null || afterLastSpace.length() == 0) { + suggestFilesInDir(suggestions, dir, beforeLastSpace ); return; } @@ -337,12 +339,12 @@ public class SuggestionsManager { return; } - for(SimpleMutableEntry<String, Integer> s : Compare.matchesWithRate(files, prev, false)) { - suggestions.add(new Suggestion(before, s.getKey(), false, s.getValue(), Suggestion.TYPE_FILE)); + for(SimpleMutableEntry<String, Integer> s : Compare.matchesWithRate(files, afterLastSpace, false)) { + suggestions.add(new Suggestion(beforeLastSpace , s.getKey(), false, s.getValue(), Suggestion.TYPE_FILE)); } } - private void suggestFilesInDir(List<Suggestion> suggestions, File dir, String before) { + private void suggestFilesInDir(List<Suggestion> suggestions, File dir, String beforeLastSpace ) { if (dir == null || !dir.isDirectory()) { return; } @@ -354,57 +356,57 @@ public class SuggestionsManager { } Arrays.sort(files); for (String s : files) { - suggestions.add(new Suggestion(before, s, false, NO_RATE, Suggestion.TYPE_FILE)); + suggestions.add(new Suggestion(beforeLastSpace , s, false, NO_RATE, Suggestion.TYPE_FILE)); } } catch (NullPointerException e) {} } - private void suggestContact(MainPack info, List<Suggestion> suggestions, String prev, String before) { + private void suggestContact(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { List<ContactManager.Contact> contacts = info.contacts.getContacts(); if(contacts == null) return; - if (prev == null || prev.length() == 0) { - for (ContactManager.Contact contact : contacts) suggestions.add(new Suggestion(before, contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact)); + if (afterLastSpace == null || afterLastSpace.length() == 0) { + for (ContactManager.Contact contact : contacts) suggestions.add(new Suggestion(beforeLastSpace , contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact)); } else { for(ContactManager.Contact contact : contacts) { if(Thread.currentThread().isInterrupted()) return; - int rate = Compare.matches(contact.name, prev, true); + int rate = Compare.matches(contact.name, afterLastSpace, true); if(rate != -1) { - suggestions.add(new Suggestion(before, contact.name, true, rate, Suggestion.TYPE_CONTACT, contact)); + suggestions.add(new Suggestion(beforeLastSpace , contact.name, true, rate, Suggestion.TYPE_CONTACT, contact)); } } } } - private void suggestSong(MainPack info, List<Suggestion> suggestions, String prev, String before) { + private void suggestSong(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { if(info.player == null) return; List<Song> songs = info.player.getSongs(); if(songs == null) return; - if (prev == null || prev.length() == 0) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { for (Song s : songs) { - suggestions.add(new Suggestion(before, s.getTitle(), clickToLaunch, NO_RATE, Suggestion.TYPE_SONG)); + suggestions.add(new Suggestion(beforeLastSpace , s.getTitle(), clickToLaunch, NO_RATE, Suggestion.TYPE_SONG)); } } else { - List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(songs, true, prev); + List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(songs, true, afterLastSpace); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_SONG)); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_SONG)); } } } - private void suggestCommand(MainPack info, List<Suggestion> suggestions, String prev, String before) { - if (prev == null || prev.length() == 0) { - suggestCommand(info, suggestions, before); + private void suggestCommand(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { + suggestCommand(info, suggestions, beforeLastSpace ); return; } - if(prev.length() <= FIRST_INTERVAL) { - prev = prev.toLowerCase().trim(); + if(afterLastSpace.length() <= FIRST_INTERVAL) { + afterLastSpace = afterLastSpace.toLowerCase().trim(); String[] cmds = info.commandGroup.getCommandNames(); if(cmds == null) return; @@ -412,23 +414,23 @@ public class SuggestionsManager { for (String s : cmds) { if(Thread.currentThread().isInterrupted()) return; - if(s.startsWith(prev)) { + if(s.startsWith(afterLastSpace)) { CommandAbstraction cmd = info.commandGroup.getCommandByName(s); int[] args = cmd.argType(); boolean exec = args == null || args.length == 0; - suggestions.add(new Suggestion(before, s, exec && clickToLaunch, MAX_RATE, Suggestion.TYPE_COMMAND)); + suggestions.add(new Suggestion(beforeLastSpace , s, exec && clickToLaunch, MAX_RATE, Suggestion.TYPE_COMMAND)); } } } } - private void suggestColor(List<Suggestion> suggestions, String prev, String before) { - if(prev == null || prev.length() == 0 || (prev.length() == 1 && prev.charAt(0) != '#')) { - suggestions.add(new Suggestion(before, "#", false, MAX_RATE, Suggestion.TYPE_COLOR)); + private void suggestColor(List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { + if(afterLastSpace == null || afterLastSpace.length() == 0 || (afterLastSpace.length() == 1 && afterLastSpace.charAt(0) != '#')) { + suggestions.add(new Suggestion(beforeLastSpace , "#", false, MAX_RATE, Suggestion.TYPE_COLOR)); } } - private void suggestCommand(MainPack info, List<Suggestion> suggestions, String before) { + private void suggestCommand(MainPack info, List<Suggestion> suggestions, String beforeLastSpace ) { String[] cmds = info.commandGroup.getCommandNames(); if(cmds == null) return; @@ -439,82 +441,82 @@ public class SuggestionsManager { if (cmd != null && cmd.priority() >= MIN_COMMAND_PRIORITY) { int[] args = cmd.argType(); boolean exec = args == null || args.length == 0; - suggestions.add(new Suggestion(before, s, exec && clickToLaunch, cmd.priority(), Suggestion.TYPE_COMMAND)); + suggestions.add(new Suggestion(beforeLastSpace , s, exec && clickToLaunch, cmd.priority(), Suggestion.TYPE_COMMAND)); } } } - private void suggestApp(MainPack info, List<Suggestion> suggestions, String prev, String before) { + private void suggestApp(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace) { List<AppsManager.LaunchInfo> apps = info.appsManager.shownApps(); if(apps == null) return; - if (prev == null || prev.length() == 0) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { for (AppsManager.LaunchInfo l : apps) { - suggestions.add(new Suggestion(before, l.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, l)); + suggestions.add(new Suggestion(beforeLastSpace , l.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, l)); } } else { - List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, prev); + List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, afterLastSpace); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); } } } - private void suggestHiddenApp(MainPack info, List<Suggestion> suggestions, String prev, String before) { + private void suggestHiddenApp(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace) { List<AppsManager.LaunchInfo> apps = info.appsManager.hiddenApps(); if(apps == null) return; - if (prev == null || prev.length() == 0) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { for (AppsManager.LaunchInfo a : apps) { - suggestions.add(new Suggestion(before, a.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP)); + suggestions.add(new Suggestion(beforeLastSpace , a.publicLabel, false, NO_RATE, Suggestion.TYPE_APP)); } } else { - List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, prev); + List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, afterLastSpace); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP)); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), false, i.getValue(), Suggestion.TYPE_APP)); } } } - private void suggestAllPackages(MainPack info, List<Suggestion> suggestions, String prev, String before) { + private void suggestAllPackages(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { List<AppsManager.LaunchInfo> apps = new ArrayList<>(info.appsManager.shownApps()); apps.addAll(info.appsManager.hiddenApps()); - if (prev == null || prev.length() == 0) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { for (AppsManager.LaunchInfo a : apps) { - suggestions.add(new Suggestion(before, a.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, a)); + suggestions.add(new Suggestion(beforeLastSpace , a.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, a)); } } else { - List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, prev); + List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, afterLastSpace); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); } } } - private void suggestDefaultApp(MainPack info, List<Suggestion> suggestions, String prev, String before) { - suggestions.add(new Suggestion(before, "most_used", false, MAX_RATE, Suggestion.TYPE_PERMANENT)); - suggestions.add(new Suggestion(before, "null", false, MAX_RATE, Suggestion.TYPE_PERMANENT)); + private void suggestDefaultApp(MainPack info, List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { + suggestions.add(new Suggestion(beforeLastSpace , "most_used", false, MAX_RATE, Suggestion.TYPE_PERMANENT)); + suggestions.add(new Suggestion(beforeLastSpace , "null", false, MAX_RATE, Suggestion.TYPE_PERMANENT)); List<AppsManager.LaunchInfo> apps = info.appsManager.shownApps(); if(apps == null) return; - if (prev == null || prev.length() == 0) { + if (afterLastSpace == null || afterLastSpace.length() == 0) { for (AppsManager.LaunchInfo a : apps) { - suggestions.add(new Suggestion(before, a.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, a)); + suggestions.add(new Suggestion(beforeLastSpace , a.publicLabel, clickToLaunch, NO_RATE, Suggestion.TYPE_APP, a)); } } else { - List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, prev); + List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, afterLastSpace); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); } } } - private void suggestConfigEntry(List<Suggestion> suggestions, String prev, String before) { + private void suggestConfigEntry(List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { if(xmlPrefsEntrys == null) { xmlPrefsEntrys = new ArrayList<>(); @@ -522,11 +524,12 @@ public class SuggestionsManager { Collections.addAll(xmlPrefsEntrys, Apps.values()); Collections.addAll(xmlPrefsEntrys, Notifications.values()); + Collections.addAll(xmlPrefsEntrys, Rss.values()); } - if(prev == null || prev.length() == 0) { + if(afterLastSpace == null || afterLastSpace.length() == 0) { for(XMLPrefsManager.XMLPrefsSave s : xmlPrefsEntrys) { - Suggestion sg = new Suggestion(before, s.label(), false, NO_RATE, Suggestion.TYPE_COMMAND); + Suggestion sg = new Suggestion(beforeLastSpace , s.label(), false, NO_RATE, Suggestion.TYPE_COMMAND); suggestions.add(sg); } } @@ -535,86 +538,87 @@ public class SuggestionsManager { if(Thread.currentThread().isInterrupted()) return; String label = s.label(); - int rate = Compare.matches(label, prev, true); + int rate = Compare.matches(label, afterLastSpace, true); if(rate != -1) { - suggestions.add(new Suggestion(before, label, false, rate, Suggestion.TYPE_COMMAND)); + suggestions.add(new Suggestion(beforeLastSpace , label, false, rate, Suggestion.TYPE_COMMAND)); } } } } - private void suggestConfigFile(List<Suggestion> suggestions, String prev, String before) { + private void suggestConfigFile(List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { if(xmlPrefsFiles == null) { xmlPrefsFiles = new ArrayList<>(); for(XMLPrefsManager.XMLPrefsRoot element : XMLPrefsManager.XMLPrefsRoot.values()) xmlPrefsFiles.add(element.path); xmlPrefsFiles.add(AppsManager.PATH); xmlPrefsFiles.add(NotificationManager.PATH); + xmlPrefsFiles.add(RssManager.PATH); } - if(prev == null || prev.length() == 0) { + if(afterLastSpace == null || afterLastSpace.length() == 0) { for(String s : xmlPrefsFiles) { - Suggestion sg = new Suggestion(before, s, false, NO_RATE, Suggestion.TYPE_FILE); + Suggestion sg = new Suggestion(beforeLastSpace , s, false, NO_RATE, Suggestion.TYPE_FILE); suggestions.add(sg); } - } else if(prev.length() <= FIRST_INTERVAL) { - prev = prev.trim().toLowerCase(); + } else if(afterLastSpace.length() <= FIRST_INTERVAL) { + afterLastSpace = afterLastSpace.trim().toLowerCase(); for (String s : xmlPrefsFiles) { if(Thread.currentThread().isInterrupted()) return; - if(s.startsWith(prev)) { - suggestions.add(new Suggestion(before, s, false, MAX_RATE, Suggestion.TYPE_FILE)); + if(s.startsWith(afterLastSpace)) { + suggestions.add(new Suggestion(beforeLastSpace , s, false, MAX_RATE, Suggestion.TYPE_FILE)); } } } } - private void suggestAppGroup(List<Suggestion> suggestions, String prev, String before) { + private void suggestAppGroup(List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace ) { List<AppsManager.Group> groups = AppsManager.groups; - if(prev == null || prev.length() == 0) { + if(afterLastSpace == null || afterLastSpace.length() == 0) { for(AppsManager.Group g : groups) { - Suggestion sg = new Suggestion(before, g.getName(), false, NO_RATE, Suggestion.TYPE_APP, g); + Suggestion sg = new Suggestion(beforeLastSpace , g.getName(), false, NO_RATE, Suggestion.TYPE_APP, g); suggestions.add(sg); } } else { for(AppsManager.Group g : groups) { String label = g.getName(); - int rate = Compare.matches(label, prev, true); + int rate = Compare.matches(label, afterLastSpace, true); if(rate != -1) { - suggestions.add(new Suggestion(before, label, false, rate, Suggestion.TYPE_APP, g)); + suggestions.add(new Suggestion(beforeLastSpace , label, false, rate, Suggestion.TYPE_APP, g)); } } } } - private boolean suggestAppInsideGroup(List<Suggestion> suggestions, String prev, String before, boolean keepGroupName) { + private boolean suggestAppInsideGroup(List<Suggestion> suggestions, String afterLastSpace, String beforeLastSpace , boolean keepGroupName) { int index = -1; String app = Tuils.EMPTYSTRING; - if(!before.contains(Tuils.SPACE)) { - index = Tuils.find(before, AppsManager.groups); - app = prev; - if(!keepGroupName) before = Tuils.EMPTYSTRING; + if(!beforeLastSpace .contains(Tuils.SPACE)) { + index = Tuils.find(beforeLastSpace , AppsManager.groups); + app = afterLastSpace; + if(!keepGroupName) beforeLastSpace = Tuils.EMPTYSTRING; } else { - String[] split = before.split(Tuils.SPACE); + String[] split = beforeLastSpace .split(Tuils.SPACE); for(int count = 0; count < split.length; count++) { index = Tuils.find(split[count], AppsManager.groups); if(index != -1) { - before = Tuils.EMPTYSTRING; + beforeLastSpace = Tuils.EMPTYSTRING; for(int i = 0; (keepGroupName ? i <= count : i < count); i++) { - before = before + split[i] + Tuils.SPACE; + beforeLastSpace = beforeLastSpace + split[i] + Tuils.SPACE; } - before = before.trim(); + beforeLastSpace = beforeLastSpace .trim(); count += 1; for(; count < split.length; count++) { app = app + split[count] + Tuils.SPACE; } - if(prev != null) app = app + Tuils.SPACE + prev; + if(afterLastSpace != null) app = app + Tuils.SPACE + afterLastSpace; app = app.trim(); break; @@ -630,13 +634,13 @@ public class SuggestionsManager { if(apps != null && apps.size() > 0) { if (app == null || app.length() == 0) { for (Compare.Stringable s : apps) { - suggestions.add(new Suggestion(before, s.getString(), clickToLaunch, NO_RATE, Suggestion.TYPE_APP, s)); + suggestions.add(new Suggestion(beforeLastSpace , s.getString(), clickToLaunch, NO_RATE, Suggestion.TYPE_APP, s)); } } else { List<SimpleMutableEntry<Compare.Stringable, Integer>> infos = Compare.matchesWithRate(apps, true, app); for(SimpleMutableEntry<Compare.Stringable, Integer> i : infos) { - suggestions.add(new Suggestion(before, i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); + suggestions.add(new Suggestion(beforeLastSpace , i.getKey().getString(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP, i.getKey())); } } } @@ -665,12 +669,12 @@ public class SuggestionsManager { public Object object; - public Suggestion(String before, String text, boolean exec, int rate, int type) { - this(before, text, exec, rate, type, null); + public Suggestion(String beforeLastSpace , String text, boolean exec, int rate, int type) { + this(beforeLastSpace , text, exec, rate, type, null); } - public Suggestion(String before, String text, boolean exec, int rate, int type, Object tag) { - this.textBefore = before; + public Suggestion(String beforeLastSpace , String text, boolean exec, int rate, int type, Object tag) { + this.textBefore = beforeLastSpace ; this.text = text; this.exec = exec; diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/XMLPrefsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/XMLPrefsManager.java index c493900..7dce317 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/XMLPrefsManager.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/XMLPrefsManager.java @@ -14,6 +14,7 @@ import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; @@ -24,8 +25,12 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import ohi.andre.consolelauncher.R; -import ohi.andre.consolelauncher.managers.xml.options.*; +import ohi.andre.consolelauncher.managers.xml.options.Behavior; +import ohi.andre.consolelauncher.managers.xml.options.Cmd; +import ohi.andre.consolelauncher.managers.xml.options.Suggestions; +import ohi.andre.consolelauncher.managers.xml.options.Theme; +import ohi.andre.consolelauncher.managers.xml.options.Toolbar; +import ohi.andre.consolelauncher.managers.xml.options.Ui; import ohi.andre.consolelauncher.tuils.Tuils; public class XMLPrefsManager { @@ -44,8 +49,13 @@ public class XMLPrefsManager { } public interface XMLPrefsSave { + + String APP = "app", INTEGER = "int", BOOLEAN = "boolean", TEXT = "text", COLOR = "color"; + String defaultValue(); String label(); + String type(); + String info(); XmlPrefsElement parent(); boolean is(String s); } @@ -55,13 +65,13 @@ public class XMLPrefsManager { THEME("theme.xml", Theme.values()) { @Override public String[] deleted() { - return new String[0]; + return new String[] {"rss_color"}; } }, CMD("cmd.xml", Cmd.values()) { @Override public String[] deleted() { - return new String[] {"time_format"}; + return new String[] {"time_format", "default_translatefrom", "default_translateto"}; } }, TOOLBAR("toolbar.xml", Toolbar.values()) { @@ -73,13 +83,15 @@ public class XMLPrefsManager { UI("ui.xml", Ui.values()) { @Override public String[] deleted() { - return new String[] {"show_timestamp_before_cmd", "linux_like", "show_username_ssninfo", "show_ssninfo", "show_path_ssninfo", "show_devicename_ssninfo", "show_alias_suggestions", "transparent_bars"}; + return new String[] {"status_line0_alignment", "status_line1_alignment", "status_line2_alignment", "status_line3_alignment", "status_line4_alignment", "status_line5_alignment", + "font_size", "show_timestamp_before_cmd", "linux_like", "show_username_ssninfo", "show_ssninfo", "show_path_ssninfo", "show_devicename_ssninfo", "show_alias_suggestions", + "transparent_bars"}; } }, BEHAVIOR("behavior.xml", Behavior.values()) { @Override public String[] deleted() { - return new String[] {"double_tap_closes", "donation_message"}; + return new String[] {"double_tap_closes", "donation_message", "tui_notification_cmd_label", "autolower_firstchar", "long_click_vibrate", "long_click_vibrate_duration"}; } }, SUGGESTIONS("suggestions.xml", Suggestions.values()) { @@ -109,7 +121,7 @@ public class XMLPrefsManager { @Override public void write(XMLPrefsSave save, String value) { - set(new File(Tuils.getFolder(), path), name(), save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); + set(new File(Tuils.getFolder(), path), save.label(), new String[] {VALUE_ATTRIBUTE}, new String[] {value}); } public XMLPrefsList getValues() { @@ -183,6 +195,8 @@ public class XMLPrefsManager { static boolean called = false; public static void create(Context context) throws Exception { + boolean timeFound = false; + if(called) return; called = true; @@ -197,9 +211,12 @@ public class XMLPrefsManager { Object[] o; try { o = buildDocument(file, element.name()); + if(o == null) { + Tuils.sendXMLParseError(context, element.path); + return; + } } catch (Exception e) { - Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + element.path + context.getString(R.string.output_xmlproblem2) + - Tuils.NEWLINE + context.getString(R.string.output_errorlabel) + e.toString()); + Tuils.sendXMLParseError(context, element.path, e); continue; } @@ -221,9 +238,137 @@ public class XMLPrefsManager { for(int count = 0; count < nodes.getLength(); count++) { Node node = nodes.item(count); - String nn = node.getNodeName(); - element.values.add(nn, node.getAttributes().getNamedItem(VALUE_ATTRIBUTE).getNodeValue()); + String value = node.getAttributes().getNamedItem(VALUE_ATTRIBUTE).getNodeValue(); + +// remove this in a month? + if(!timeFound && nn.equals(Behavior.time_format.label()) && value.contains("%")) { + timeFound = true; + + Pattern p = Pattern.compile("%(.{1})"); + Matcher m = p.matcher(value); + while(m.find()) { + char charc = m.group(1).charAt(0); + + String replaceWith = null; + switch (charc) { + case 'a': + replaceWith = "EE"; + break; + case 'A': + replaceWith = "E"; + break; + case 'b': + replaceWith = "MM"; + break; + case 'B': + replaceWith = "M"; + break; + case 'c': + replaceWith = "EE MM dd HH:mm:ss yyyy"; + break; + case 'C': + break; + case 'd': + replaceWith = "dd"; + break; + case 'D': + replaceWith = "MM dd yyyy"; + break; + case 'e': + replaceWith = "dd"; + break; + case 'F': + replaceWith = "yyyy-MMM-dd"; + break; + case 'g': + break; + case 'G': + break; + case 'h': + replaceWith = "MMM"; + break; + case 'H': + replaceWith = "HH"; + break; + case 'I': + replaceWith = "hh"; + break; + case 'j': + replaceWith = "DDD"; + break; + case 'k': + replaceWith = "HH"; + break; + case 'l': + replaceWith = "hh"; + break; + case 'm': + replaceWith = "MM"; + break; + case 'M': + replaceWith = "mm"; + break; + case 'n': + break; + case 'N': + break; + case 'p': + replaceWith = "a"; + break; + case 'P': + break; + case 'r': + replaceWith = "KK:mm:ss a"; + break; + case 'R': + replaceWith = "HH:mm"; + break; + case 's': + break; + case 'S': + replaceWith = "s"; + break; + case 't': + break; + case 'T': + replaceWith = "HH:mm:ss"; + break; + case 'u': + replaceWith = "u"; + break; + case 'U': + replaceWith = "w"; + break; + case 'V': + break; + case 'w': + replaceWith = "u"; + break; + case 'W': + replaceWith = "w"; + break; + case 'x': + replaceWith = "MMM/dd/yyyy"; + break; + case 'X': + replaceWith = "HH:mm:ss"; + break; + case 'y': + replaceWith = "yy"; + break; + case 'Y': + replaceWith = "yyyy"; + break; + } + + if(replaceWith != null) value = value.replaceAll(m.group(0), replaceWith); + } + + ((Element) node).setAttribute(VALUE_ATTRIBUTE, value); + } + + element.values.add(nn, value); for(int en = 0; en < enums.size(); en++) { if(enums.get(en).label().equals(nn)) { @@ -296,12 +441,24 @@ public class XMLPrefsManager { return s; } +// rootName is needed in order to rebuild the file if it's corrupted // [0] = document // [1] = root - public static Object[] buildDocument(File file, String root) throws Exception { - Document d = builder.parse(file); - Element r = (Element) d.getElementsByTagName(root).item(0); + public static Object[] buildDocument(File file, String rootName) throws Exception { + Document d; + try { + d = builder.parse(file); + } catch (Exception e) { + Tuils.log(e); + int nOfBytes = Tuils.nOfBytes(file); + Tuils.log("nof", nOfBytes); + if(nOfBytes == 0 && rootName != null) { + XMLPrefsManager.resetFile(file, rootName); + d = builder.parse(file); + } else return null; + } + Element r = d.getDocumentElement(); return new Object[] {d, r}; } @@ -322,15 +479,18 @@ public class XMLPrefsManager { stream.flush(); stream.close(); - } catch (Exception e) {} + } catch (Exception e) { + Tuils.log(e); + } } // this will only add, it won't check if there's already one - public static String add(File file, String rootName, String elementName, String[] attributeNames, String[] attributeValues) { + public static String add(File file, String elementName, String[] attributeNames, String[] attributeValues) { try { Object[] o; try { - o = buildDocument(file, rootName); + o = buildDocument(file, null); + if(o == null) return Tuils.EMPTYSTRING; } catch (Exception e) { Tuils.log(e); return e.toString(); @@ -354,33 +514,42 @@ public class XMLPrefsManager { return null; } - public static String set(File file, String rootName, String elementName, String[] attributeNames, String[] attributeValues) { - return set(file, rootName, elementName, null, null, attributeNames, attributeValues, true); + public static String set(File file, String elementName, String[] attributeNames, String[] attributeValues) { + return set(file, elementName, null, null, attributeNames, attributeValues, true); } - public static String set(File file, String rootName, String elementName, String[] thatHasThose, String[] forValues, String[] attributeNames, String[] attributeValues, boolean addIfNotFound) { + public static String set(File file, String elementName, String[] thatHasThose, String[] forValues, String[] attributeNames, String[] attributeValues, boolean addIfNotFound) { String[][] values = new String[1][attributeValues.length]; values[0] = attributeValues; - return setMany(file, rootName, new String[] {elementName}, thatHasThose, forValues, attributeNames, values, addIfNotFound); + return setMany(file, new String[] {elementName}, thatHasThose, forValues, attributeNames, values, addIfNotFound); } - public static String setMany(File file, String rootName, String elementNames[], String[] attributeNames, String[][] attributeValues) { - return setMany(file, rootName, elementNames, null, null, attributeNames, attributeValues, true); + public static String setMany(File file, String elementNames[], String[] attributeNames, String[][] attributeValues) { + return setMany(file, elementNames, null, null, attributeNames, attributeValues, true); } - public static String setMany(File file, String rootName, String elementNames[], String[] thatHasThose, String[] forValues, String[] attributeNames, String[][] attributeValues, boolean addIfNotFound) { + public static String setMany(File file, String elementNames[], String[] thatHasThose, String[] forValues, String[] attributeNames, String[][] attributeValues, boolean addIfNotFound) { try { Object[] o; try { - o = buildDocument(file, rootName); + o = buildDocument(file, null); + if(o == null) return Tuils.EMPTYSTRING; } catch (Exception e) { + Tuils.log(e); return e.toString(); } Document d = (Document) o[0]; Element root = (Element) o[1]; + if(d == null || root == null) { + Tuils.log("document is null or root is null"); + return Tuils.EMPTYSTRING; + } + + int nFound = 0; + Main: for(int c = 0; c < elementNames.length; c++) { NodeList nodes = root.getElementsByTagName(elementNames[c]); @@ -391,57 +560,60 @@ public class XMLPrefsManager { if(n.getNodeType() == Node.ELEMENT_NODE) { Element e = (Element) n; - if(checkAttributes(e, thatHasThose, forValues)) continue Nodes; + if(!checkAttributes(e, thatHasThose, forValues)) { + continue Nodes; + } + + nFound++; for(int a = 0; a < attributeNames.length; a++) { e.setAttribute(attributeNames[a], attributeValues[c][a]); } elementNames[c] = null; + continue Main; } } } - boolean notFoundSomething = false; + if(nFound < elementNames.length) { + for (int count = 0; count < elementNames.length; count++) { + if (elementNames[count] == null || elementNames[count].length() == 0) continue; -// not found - for(int count = 0; count < elementNames.length; count++) { - if(elementNames[count] == null || elementNames[count].length() == 0) continue; + if (!addIfNotFound) continue; - notFoundSomething = true; - - if(!addIfNotFound) continue; - - Element element = d.createElement(elementNames[count]); - for(int c = 0; c < attributeNames.length; c++) { - if(attributeValues[count][c] == null) continue; - element.setAttribute(attributeNames[c], attributeValues[count][c]); + Element element = d.createElement(elementNames[count]); + for (int c = 0; c < attributeNames.length; c++) { + if (attributeValues[count][c] == null) continue; + element.setAttribute(attributeNames[c], attributeValues[count][c]); + } + root.appendChild(element); } - root.appendChild(element); } writeTo(d, file); - if(notFoundSomething) return Tuils.EMPTYSTRING; + if(nFound == 0) return Tuils.EMPTYSTRING; + return null; } catch (Exception e) { Tuils.log(e); Tuils.toFile(e); return e.toString(); } - return null; } // return "" if node not found, null if all good - public static String removeNode(File file, String rootName, String nodeName) { - return removeNode(file, rootName, nodeName, null, null); + public static String removeNode(File file, String nodeName) { + return removeNode(file, nodeName, null, null); } - public static String removeNode(File file, String rootName, String nodeName, String[] thatHasThose, String[] forValues) { + public static String removeNode(File file, String nodeName, String[] thatHasThose, String[] forValues) { try { Object[] o; try { - o = buildDocument(file, rootName); + o = buildDocument(file, null); + if(o == null) return Tuils.EMPTYSTRING; } catch (Exception e) { return e.toString(); } @@ -449,70 +621,71 @@ public class XMLPrefsManager { Document d = (Document) o[0]; Element root = (Element) o[1]; - NodeList nodeList = root.getElementsByTagName(nodeName); - if(nodeList == null || nodeList.getLength() == 0) return Tuils.EMPTYSTRING; - - for(int c = 0; c < nodeList.getLength(); c++) { - Node n = nodeList.item(c); + Node n = findNode(root, nodeName, thatHasThose, forValues); + if(n == null) return Tuils.EMPTYSTRING; - if (n.getNodeType() == Node.ELEMENT_NODE && !checkAttributes((Element) n, thatHasThose, forValues)) - continue; + root.removeChild(n); + writeTo(d, file); - root.removeChild(n); - writeTo(d, file); - return null; - } + return null; } catch (Exception e) { return e.toString(); } + } - return Tuils.EMPTYSTRING; + public static Node findNode(File file, String nodeName) { + return findNode(file, nodeName, null, null); } - public static Node findNode(File file, String rootName, String nodeName) { + public static Node findNode(File file, String nodeName, String[] thatHasThose, String[] forValues) { try { Object[] o; try { - o = buildDocument(file, rootName); + o = buildDocument(file, null); + if(o == null) return null; } catch (Exception e) { return null; } Element root = (Element) o[1]; - findNode(root, nodeName); + return findNode(root, nodeName, thatHasThose, forValues); } catch (Exception e) { return null; } - - return null; } public static Node findNode(Element root, String nodeName) { + return findNode(root, nodeName, null, null); + } + +// useful only if you're looking for a single node + public static Node findNode(Element root, String nodeName, String[] thatHasThose, String[] forValues) { NodeList nodes = root.getElementsByTagName(nodeName); - if(nodes.getLength() == 0) return null; - return nodes.item(0); + for(int j = 0; j < nodes.getLength(); j++) if(checkAttributes((Element) nodes.item(j), thatHasThose, forValues)) return nodes.item(j); + return null; } - public static String attrValue(File file, String rootName, String nodeName, String attrName) { - return attrValue(file, rootName, nodeName, null, null, attrName); + public static String attrValue(File file, String nodeName, String attrName) { + return attrValue(file, nodeName, null, null, attrName); } - public static String attrValue(File file, String rootName, String nodeName, String[] thatHasThose, String[] forValues, String attrName) { - String[] vs = attrValues(file, rootName, nodeName, thatHasThose, forValues, new String[] {attrName}); + public static String attrValue(File file, String nodeName, String[] thatHasThose, String[] forValues, String attrName) { + String[] vs = attrValues(file, nodeName, thatHasThose, forValues, new String[] {attrName}); if(vs != null && vs.length > 0) return vs[0]; return null; } - public static String[] attrValues(File file, String rootName, String nodeName, String[] attrNames) { - return attrValues(file, rootName, nodeName, null, null, attrNames); + public static String[] attrValues(File file, String nodeName, String[] attrNames) { + return attrValues(file, nodeName, null, null, attrNames); } - public static String[] attrValues(File file, String rootName, String nodeName, String[] thatHasThose, String[] forValues, String[] attrNames) { + public static String[] attrValues(File file, String nodeName, String[] thatHasThose, String[] forValues, String[] attrNames) { try { Object[] o; try { - o = buildDocument(file, rootName); + o = buildDocument(file, null); + if(o == null) return null; } catch (Exception e) { return null; } @@ -539,7 +712,8 @@ public class XMLPrefsManager { private static boolean checkAttributes(Element e, String[] thatHasThose, String[] forValues) { if(thatHasThose != null && forValues != null && thatHasThose.length == forValues.length) { for(int a = 0; a < thatHasThose.length; a++) { - if(!e.hasAttribute(thatHasThose[a]) || !forValues[a].equals(e.getAttribute(thatHasThose[a]))) return false; + if(!e.hasAttribute(thatHasThose[a])) return false; + if(!forValues[a].equals(e.getAttribute(thatHasThose[a]))) return false; } } return true; @@ -586,6 +760,7 @@ public class XMLPrefsManager { public static <T> T get(Class<T> c, XMLPrefsManager.XMLPrefsSave prefsSave) { try { + Tuils.log("list", prefsSave.parent().getValues().values().toString()); return (T) transform(prefsSave.parent().getValues().get(prefsSave).value, c); } catch (Exception e) { Tuils.log(e); @@ -616,6 +791,30 @@ public class XMLPrefsManager { } } + public static String getStringAttribute(Element e, String attribute) { + return e.hasAttribute(attribute) ? e.getAttribute(attribute) : null; + } + + public static long getLongAttribute(Element e, String attribute) { + String value = getStringAttribute(e, attribute); + if(value == null) return -1; + return Long.parseLong(value); + } + + public static boolean getBooleanAttribute(Element e, String attribute) { + return Boolean.parseBoolean(getStringAttribute(e, attribute)); + } + + public static class IdValue { + public String value; + public int id; + + public IdValue(String value, int id) { + this.value = value; + this.id = id; + } + } + // private static HashMap<XMLPrefsSave, String> getOld(BufferedReader reader) { // HashMap<XMLPrefsSave, String> map = new HashMap<>(); // diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Apps.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Apps.java index d775371..5068a24 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Apps.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Apps.java @@ -14,30 +14,55 @@ public enum Apps implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return MOST_USED; } + + @Override + public String info() { + return "The first default-suggested app"; + } }, default_app_n2 { @Override public String defaultValue() { return MOST_USED; } + + @Override + public String info() { + return "The second default-suggested app"; + } }, default_app_n3 { @Override public String defaultValue() { return "com.android.vending"; } + + @Override + public String info() { + return "The third default-suggested app"; + } }, default_app_n4 { @Override public String defaultValue() { return NULL; } + + @Override + public String info() { + return "The fourth default-suggested app"; + } }, default_app_n5 { @Override public String defaultValue() { return NULL; } + + @Override + public String info() { + return "The fifth default-suggested app"; + } }; public static final String MOST_USED = "most_used"; @@ -57,4 +82,9 @@ public enum Apps implements XMLPrefsManager.XMLPrefsSave { public boolean is(String s) { return name().equals(s); } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.APP; + } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Behavior.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Behavior.java index ae2e396..a72ddfe 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Behavior.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Behavior.java @@ -18,89 +18,255 @@ public enum Behavior implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, t-ui will lock the screen on double tap"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, double_tap_cmd { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "The command that will run when you touch two times the screen quickly"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, random_play { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, music player will play your tracks in random order"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, songs_folder { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "The folder that contains your music files"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, songs_from_mediastore { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, t-ui will get tracks from the system mediastore"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, tui_notification { @Override public String defaultValue() { return "false"; } + + @Override + public String info() { + return "If true, there will always be a notification in your status bar, telling you that t-ui is running"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, auto_show_keyboard { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, your keyboard will be shown everytime you go back to t-ui"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, auto_scroll { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, the terminal will be automatically scrolled down when the keyboard is open"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, show_hints { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, t-ui will tell you some useful hints sometime"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, show_alias_content { @Override public String defaultValue() { return "false"; } + + @Override + public String info() { + return "If true, when you use an alias you'll also be able to know what command has been executed"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, show_launch_history { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If false, t-ui won't show the apps that you launch"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, clear_after_cmds { @Override public String defaultValue() { return "-1"; } + + @Override + public String info() { + return "Auto-clear after n commands (if -1, this feature will be disabled)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, clear_after_seconds { @Override public String defaultValue() { return "-1"; } + + @Override + public String info() { + return "Auto-clear after n seconds (if -1, this feature will be disabled)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, max_lines { @Override public String defaultValue() { return "-1"; } + + @Override + public String info() { + return "Set maximum number of lines that will be shown in the terminal (if -1, this feature is be disabled)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, time_format { @Override public String defaultValue() { - return "%m/%d/%y %H.%M"; + return "d MMM yyyy HH:mm:ss@HH:mm:ss"; + } + + @Override + public String info() { + return "Define the time format (see also Time Format)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + }, + time_format_separator { + @Override + public String defaultValue() { + return "@"; + } + + @Override + public String info() { + return "This is the separator between your different time formats (see also Multiple time formats)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; } }, battery_medium { @@ -108,77 +274,191 @@ public enum Behavior implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "50"; } + + @Override + public String info() { + return "The percentage below which the battery level will be considered \"medium\""; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, battery_low { @Override public String defaultValue() { return "15"; } + + @Override + public String info() { + return "The percentage below which the battery level will be considered \"low\""; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, device_format { @Override public String defaultValue() { return "%d: %u"; } + + @Override + public String info() { + return "Define the device format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, ram_format { @Override public String defaultValue() { return "Available RAM: %avgb GB of %totgb GB (%av%%)"; } + + @Override + public String info() { + return "Define the RAM format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, battery_format { @Override public String defaultValue() { return "%(Charging: /)%v%"; } + + @Override + public String info() { + return "Define the battery format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, storage_format { @Override public String defaultValue() { return "Internal Storage: %iavgb GB / %itotgb GB (%iav%%)"; } + + @Override + public String info() { + return "Define the storage format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, network_info_format { @Override public String defaultValue() { return "%(WiFi - %wn/%[Mobile Data: %d3/No Internet access])"; } + + @Override + public String info() { + return "Define the network format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, input_format { @Override public String defaultValue() { return "[%t] %p %i"; } + + @Override + public String info() { + return "Define the input format "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, output_format { @Override public String defaultValue() { return "%o"; } + + @Override + public String info() { + return "Define the output format "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, session_info_format { @Override public String defaultValue() { return "%u@%d:%p"; } + + @Override + public String info() { + return "Define the session info format"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, enable_app_launch { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If false, you won't be able to launch apps from t-ui, unless you use \"apps -frc\""; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }, app_launch_format { @Override public String defaultValue() { return "--> %a"; } - }, - time_format_separator { + @Override - public String defaultValue() { - return "@"; + public String info() { + return "Define app launch format "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; } }, alias_param_marker { @@ -186,24 +466,64 @@ public enum Behavior implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "%"; } + + @Override + public String info() { + return "Define the marker that will be replaced with a provided param"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, alias_param_separator { @Override public String defaultValue() { return ","; } + + @Override + public String info() { + return "Define the separator between a group of params"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, multiple_cmd_separator { @Override public String defaultValue() { return ";"; } + + @Override + public String info() { + return "The separator between two or more commands in a single input"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, alias_content_format { @Override public String defaultValue() { return "%a --> [%v]"; } + + @Override + public String info() { + return "Define the format used to show your alias contents "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, external_storage_path { @Override @@ -216,48 +536,288 @@ public enum Behavior implements XMLPrefsManager.XMLPrefsSave { return Tuils.EMPTYSTRING; } + + @Override + public String info() { + return "The path to your external storage (used to evaluate free/total space)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, home_path { @Override public String defaultValue() { return Environment.getExternalStorageDirectory().getAbsolutePath(); } + + @Override + public String info() { + return "The path to your home directory"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, app_installed_format { @Override public String defaultValue() { return "App installed: %p"; } + + @Override + public String info() { + return "The format of the \"app installed\" message "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, app_uninstalled_format { @Override public String defaultValue() { return "App uninstalled: %p"; } + + @Override + public String info() { + return "The format of the \"app uninstalled\" message "; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }, enable_music { @Override public String defaultValue() { return "true"; } - }, - autolower_firstchar { + @Override - public String defaultValue() { - return "true"; + public String info() { + return "If false, t-ui won't try to load music from your device on startup"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; } }, - max_optional_depth_network_info { + max_optional_depth { @Override public String defaultValue() { return "2"; } + + @Override + public String info() { + return "A value which is used to tell how deep t-ui can go in a nested optional value"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } }, network_info_update_ms { @Override public String defaultValue() { return "3500"; } + + @Override + public String info() { + return "The time between two network info updates"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + }, + tui_notification_title { + @Override + public String defaultValue() { + return "T-UI"; + } + + @Override + public String info() { + return "The title of the T-UI notification"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + }, + tui_notification_subtitle { + @Override + public String defaultValue() { + return "T-UI is running"; + } + + @Override + public String info() { + return "The subtitle of the T-UI notification"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + }, + tui_notification_click_cmd { + @Override + public String defaultValue() { + return ""; + } + + @Override + public String info() { + return "The command ran when the T-UI notification is clicked"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + }, + tui_notification_click_showhome { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If false, the click on the T-UI notification won't bring you to your phone home"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + }, + tui_notification_lastcmds_size { + @Override + public String defaultValue() { + return "5"; + } + + @Override + public String info() { + return "The number of used commands that will appear inside the T-UI notification (<0 will disable the feature)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + }, + tui_notification_lastcmds_updown { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If true, the last used command will appear on top\n"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + }, + tui_notification_priority { + @Override + public String defaultValue() { + return "0"; + } + + @Override + public String info() { + return "The priority of the T-UI notification (-2 maximum priority, 2 minimum)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + }, + long_click_vibration_duration { + @Override + public String defaultValue() { + return "100"; + } + + @Override + public String info() { + return "The duration (in milliseconds) of the vibration when you long click a notification or an RSS item (<0 will disable the feature)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + }, + long_click_duration { + @Override + public String defaultValue() { + return "700"; + } + + @Override + public String info() { + return "The minimum duration of the long click on a notification or an RSS item"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + }, + click_commands { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If true, you will be able to use a command again clicking on it"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + }, + long_click_commands { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If true, you will be able to put a used command in the input field long-clicking it"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }; @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Cmd.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Cmd.java index 6a75bb2..b5615bf 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Cmd.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Cmd.java @@ -13,6 +13,16 @@ public enum Cmd implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "-gg"; } + + @Override + public String info() { + return "The param that will be used if you type \"search apples\" instead of \"search -param apples\""; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } }; @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Notifications.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Notifications.java index ea3fd93..0a53ad0 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Notifications.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Notifications.java @@ -14,23 +14,75 @@ public enum Notifications implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "false"; } + + @Override + public String info() { + return "If true, t-ui will show every incoming notification"; + } }, app_notification_enabled_default { @Override public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If true, t-ui will show notifications from all apps, unless they are explicitly excluded. If false, t-ui won't show a notification from a specific app unless it was \texplicitly included"; + } }, default_notification_color { @Override public String defaultValue() { return "#00FF00"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "The default color"; + } }, notification_format { @Override public String defaultValue() { - return "[%t] %pkg: %ttl --- %txt"; + return "[%t] %pkg: %[100][teal]title --- %text"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The default format"; + } + }, + click_notification { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If true, T-UI will perform the operation associated with the original notification when you click it"; + } + }, + long_click_notification { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String info() { + return "If true, you will be able to perform some quick operations long-clicking a notification"; } }; @@ -48,4 +100,9 @@ public enum Notifications implements XMLPrefsManager.XMLPrefsSave { public boolean is(String s) { return name().equals(s); } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Rss.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Rss.java index 894d1d0..7ed14ce 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Rss.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Rss.java @@ -14,11 +14,31 @@ public enum Rss implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "#f44336"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "The default color"; + } }, rss_default_format { @Override public String defaultValue() { - return "%[50]title --- %[100]description"; + return "%[50][green]title ### %[100][teal]description (%pubDate)"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The default format"; } }, include_rss_default { @@ -26,19 +46,120 @@ public enum Rss implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "If true, a filter will exclude an item if it matches. If false, a filter will include an item if it matches"; + } }, rss_hidden_tags { @Override public String defaultValue() { return "img"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "A list of excluded tags (separated by comma)"; + } }, rss_time_format { @Override public String defaultValue() { return "%t0"; } - }; + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The time format used by RSS items"; + } + }, + show_rss_download { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "If true, you will see a message when T-UI downloads a feed"; + } + }, + rss_download_format { + @Override + public String defaultValue() { + return "RSS: %id --- Downloaded %sb bytes"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The message shown when an RSS feed is downloaded"; + } + }, + rss_download_message_color { + @Override + public String defaultValue() { + return "aqua"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "The color of the download message"; + } + }, + click_rss { + @Override + public String defaultValue() { + return "true"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } + + @Override + public String info() { + return "If true, you will be able to click on an RSS item to open the associated webpage"; + } + }, +// long_click_rss { +// @Override +// public String defaultValue() { +// return "true"; +// } +// } + ; @Override public XMLPrefsManager.XmlPrefsElement parent() { diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Suggestions.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Suggestions.java index 62fff83..3097d59 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Suggestions.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Suggestions.java @@ -13,120 +13,250 @@ public enum Suggestions implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, suggestions won't be shown"; + } }, transparent_suggestions { @Override public String defaultValue() { return "false"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, the background will be transparent"; + } }, default_text_color { @Override public String defaultValue() { return "#000000"; } + + @Override + public String info() { + return "The default text color"; + } }, default_bg_color { @Override public String defaultValue() { return "#ffffff"; } + + @Override + public String info() { + return "The default background color"; + } }, apps_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Apps suggestions text color"; + } }, apps_bg_color { @Override public String defaultValue() { return "#00897B"; } + + @Override + public String info() { + return "Apps suggestions background color"; + } }, alias_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Aliases suggestions text color"; + } }, alias_bg_color { @Override public String defaultValue() { return "#FF5722"; } + + @Override + public String info() { + return "Aliases suggestions background color"; + } }, cmd_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Commands suggestions text color"; + } }, cmd_bg_color { @Override public String defaultValue() { return "#76FF03"; } + + @Override + public String info() { + return "Commands suggestions background color"; + } }, song_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Songs suggestions text color"; + } }, song_bg_color { @Override public String defaultValue() { return "#EEFF41"; } + + @Override + public String info() { + return "Songs suggestions background color"; + } }, contact_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Contacts suggestions text color"; + } }, contact_bg_color { @Override public String defaultValue() { return "#64FFDA"; } + + @Override + public String info() { + return "Contacts suggestions background color"; + } }, file_text_color { @Override public String defaultValue() { return ""; } + + @Override + public String info() { + return "Files suggestions text color"; + } }, file_bg_color { @Override public String defaultValue() { return "#03A9F4"; } + + @Override + public String info() { + return "Files suggestions background color"; + } }, suggest_alias_default { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, your alias will be shown when the input field is empty"; + } }, suggest_appgp_default { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, your app groups will be shown when the input field is empty"; + } }, click_to_launch { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, some suggestions will be executed as soon as you click them"; + } }, suggestions_size { @Override public String defaultValue() { return "12"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The text size of the suggestions"; + } }; @Override @@ -143,4 +273,9 @@ public enum Suggestions implements XMLPrefsManager.XMLPrefsSave { public boolean is(String s) { return name().equals(s); } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } } \ No newline at end of file diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Theme.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Theme.java index 31f138d..b05c430 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Theme.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Theme.java @@ -13,131 +13,241 @@ public enum Theme implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "#ff00ff00"; } + + @Override + public String info() { + return "Input color"; + } }, output_color { @Override public String defaultValue() { return "#ffffffff"; } + + @Override + public String info() { + return "Output color"; + } }, bg_color { @Override public String defaultValue() { return "#ff000000"; } + + @Override + public String info() { + return "Background color"; + } }, device_color { @Override public String defaultValue() { return "#ffff9800"; } + + @Override + public String info() { + return "Device label color"; + } }, battery_color_high { @Override public String defaultValue() { return "#4CAF50"; } + + @Override + public String info() { + return "Battery label color when the battery level is high"; + } }, battery_color_medium { @Override public String defaultValue() { return "#FFEB3B"; } + + @Override + public String info() { + return "Battery label color when the battery level is medium"; + } }, battery_color_low { @Override public String defaultValue() { return "#FF5722"; } + + @Override + public String info() { + return "Battery label color when the battery level is low"; + } }, time_color { @Override public String defaultValue() { return "#03A9F4"; } + + @Override + public String info() { + return "Time label color"; + } }, storage_color { @Override public String defaultValue() { return "#9C27B0"; } + + @Override + public String info() { + return "Storage label color"; + } }, ram_color { @Override public String defaultValue() { return "#fff44336"; } + + @Override + public String info() { + return "RAM label color"; + } }, network_info_color { @Override public String defaultValue() { return "#FFCA28"; } + + @Override + public String info() { + return ""; + } }, toolbar_bg { @Override public String defaultValue() { return "#00000000"; } + + @Override + public String info() { + return "Toolbar background color"; + } }, toolbar_color { @Override public String defaultValue() { return "#ffff0000"; } + + @Override + public String info() { + return "Toolbar icons color"; + } }, enter_color { @Override public String defaultValue() { return "#ffffffff"; } + + @Override + public String info() { + return "Enter icon color"; + } + }, + cursor_color { + @Override + public String defaultValue() { + return "#ffffff"; + } + + @Override + public String info() { + return ""; + } }, overlay_color { @Override public String defaultValue() { return "#80000000"; } + + @Override + public String info() { + return "The overlay that overlaps to the background (only when system_wallpaper is true)"; + } }, alias_content_color { @Override public String defaultValue() { return "#1DE9B6"; } + + @Override + public String info() { + return "Alias content color"; + } }, statusbar_color { @Override public String defaultValue() { return "#000000"; } + + @Override + public String info() { + return "Status Bar color (5.0+)"; + } }, navigationbar_color { @Override public String defaultValue() { return "#000000"; } + + @Override + public String info() { + return "Navigation Bar color (5.0+)"; + } }, app_installed_color { @Override public String defaultValue() { return "#FF7043"; } + + @Override + public String info() { + return "App installed message color"; + } }, app_uninstalled_color { @Override public String defaultValue() { return "#FF7043"; } + + @Override + public String info() { + return "App uninstalled message color"; + } }, hint_color { @Override public String defaultValue() { return "#4CAF50"; } - }, - rss_color { + @Override - public String defaultValue() { - return null; + public String info() { + return "Hint color"; } }, mark_color { @@ -145,6 +255,11 @@ public enum Theme implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "#CDDC39"; } + + @Override + public String info() { + return "The background color that will be used as marker"; + } }; @Override @@ -161,4 +276,9 @@ public enum Theme implements XMLPrefsManager.XMLPrefsSave { public boolean is(String s) { return name().equals(s); } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.COLOR; + } } \ No newline at end of file diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Toolbar.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Toolbar.java index c5bc862..efc2ebd 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Toolbar.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Toolbar.java @@ -13,6 +13,16 @@ public enum Toolbar implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String info() { + return "If false, the toolbar is hidden"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } }; @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Ui.java b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Ui.java index 18c78bd..a0fe32e 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Ui.java +++ b/app/src/main/java/ohi/andre/consolelauncher/managers/xml/options/Ui.java @@ -15,53 +15,143 @@ public enum Ui implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "Hide/show the enter button"; + } }, system_font { @Override public String defaultValue() { return "false"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the default t-ui font (\"Lucida Console\") will be used for all texts"; + } }, ram_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The ram label font size"; + } }, battery_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The battery label font size"; + } }, device_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The device label font size"; + } }, time_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The time label font size"; + } }, storage_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The storage label font size"; + } }, network_size { @Override public String defaultValue() { return "13"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The network label font size"; + } }, input_output_size { @Override public String defaultValue() { - return "13"; + return "15"; + } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The input/output font size"; } }, input_bottom { @@ -69,204 +159,544 @@ public enum Ui implements XMLPrefsManager.XMLPrefsSave { public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the input field will be placed on top of the screen"; + } }, show_ram { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the RAM label will be hidden"; + } }, show_device_name { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the device label will be hidden"; + } }, show_battery { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the battery label will be hidden"; + } }, show_network_info { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the network info label will be hidden"; + } }, show_storage_info { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the time label will be hidden"; + } }, enable_battery_status { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, battery color will change when your battery level reach different percentages battery_color high, battery_color_medium, battery_color_low. If false, only battery_color_high is used"; + } }, show_time { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If false, the time label will be hidden"; + } }, username { @Override public String defaultValue() { return "user"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "Your username"; + } }, deviceName { @Override public String defaultValue() { return Build.DEVICE; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "Your device name"; + } }, system_wallpaper { @Override public String defaultValue() { return "false"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, your system wallpaper will be used as background"; + } }, fullscreen { @Override public String defaultValue() { return "false"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, t-ui will run in fullscreen mode"; + } }, device_index { @Override public String defaultValue() { return "0"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, ram_index { @Override public String defaultValue() { return "1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, battery_index { @Override public String defaultValue() { return "2"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, time_index { @Override public String defaultValue() { return "3"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, storage_index { @Override public String defaultValue() { return "4"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, network_index { @Override public String defaultValue() { return "5"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "This is used to order the labels on top of the screen"; + } }, - status_line0_position { + status_line0_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the first status line (<0 = left, =0 = center, >0 = right)"; + } }, - status_line1_position { + status_line1_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the second status line (<0 = left, =0 = center, >0 = right)"; + } }, - status_line2_position { + status_line2_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the third status line (<0 = left, =0 = center, >0 = right)"; + } }, - status_line3_position { + status_line3_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the fourth status line (<0 = left, =0 = center, >0 = right)"; + } }, - status_line4_position { + status_line4_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the fifth status line (<0 = left, =0 = center, >0 = right)"; + } }, - status_line5_position { + status_line5_alignment { @Override public String defaultValue() { return "-1"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The alignment of the sixth status line (<0 = left, =0 = center, >0 = right)"; + } }, input_prefix { @Override public String defaultValue() { return "$"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The prefix placed before every input"; + } }, input_root_prefix { @Override public String defaultValue() { return "#"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.TEXT; + } + + @Override + public String info() { + return "The prefix placed before a root command (\"su ...\")"; + } }, left_margin_mm { @Override public String defaultValue() { return "0"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The left margin (in millimeters)"; + } }, right_margin_mm { @Override public String defaultValue() { return "0"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The right margin (in millimeters)"; + } }, top_margin_mm { @Override public String defaultValue() { return "0"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The top margin (in millimeters)"; + } }, bottom_margin_mm { @Override public String defaultValue() { return "0"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.INTEGER; + } + + @Override + public String info() { + return "The bottom margin (in millimeters)"; + } }, ignore_bar_color { @Override public String defaultValue() { return "false"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, statusbar_color and navigationbar_color will be ignored"; + } }, show_app_installed { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, you will receive a message when you install an app"; + } }, show_app_uninstalled { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, you will receive a message when you uninstall an app"; + } }, show_session_info { @Override public String defaultValue() { return "true"; } + + @Override + public String type() { + return XMLPrefsManager.XMLPrefsSave.BOOLEAN; + } + + @Override + public String info() { + return "If true, when your input field is empty there will be a short line containing some information about the current session"; + } }; @Override diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/AllowEqualsSequence.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/AllowEqualsSequence.java index 34c551b..1757abf 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/AllowEqualsSequence.java +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/AllowEqualsSequence.java @@ -31,7 +31,7 @@ public class AllowEqualsSequence { if(i != last) counter++; entry.key = counter; - last = counter; + last = i; } } @@ -82,7 +82,7 @@ public class AllowEqualsSequence { @Override public String toString() { - return value + ": " + obj.toString(); + return "key: " + key + ": " + obj.toString(); } } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java index 925cfdc..d3ca391 100644 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java @@ -4,11 +4,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.NotificationManagerCompat; import android.support.v4.app.RemoteInput; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import ohi.andre.consolelauncher.managers.TerminalManager; import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter; +import ohi.andre.consolelauncher.tuils.interfaces.Inputable; import ohi.andre.consolelauncher.tuils.interfaces.Outputable; /** @@ -17,24 +19,35 @@ import ohi.andre.consolelauncher.tuils.interfaces.Outputable; public class InputOutputReceiver extends BroadcastReceiver { +// this class handles incoming intent to +// process input +// show custom output + public static final int WAS_MUSIC_SERVICE = 10; public static final int WAS_KEEPER_SERVICE = 11; public static final String WAS_KEY = "was"; - public static final String ACTION_CMD = "ohi.andre.consolelauncher.action_cmd"; public static final String ACTION_OUTPUT = "ohi.andre.consolelauncher.action_output"; +// this will set an input and click it + public static final String ACTION_CMD = "ohi.andre.consolelauncher.action_cmd"; +// this will set an input and NOT click it + public static final String ACTION_INPUT = "ohi.andre.consolelauncher.action_input"; public static final String TEXT = "ohi.andre.consolelauncher.text"; public static final String TYPE = "ohi.andre.consolelauncher.type"; public static final String COLOR = "ohi.andre.consolelauncher.color"; + public static final String ACTION = "ohi.andre.consolelauncher.action"; + public static final String LONG_ACTION = "ohi.andre.consolelauncher.longaction"; public static final String SHOW_CONTENT = "ohi.andre.consolelauncher.show_content"; CommandExecuter executer; Outputable outputable; + Inputable inputable; - public InputOutputReceiver(CommandExecuter executer, Outputable outputable) { + public InputOutputReceiver(CommandExecuter executer, Outputable outputable, Inputable inputable) { this.executer = executer; this.outputable = outputable; + this.inputable = inputable; } @Override @@ -47,27 +60,42 @@ public class InputOutputReceiver extends BroadcastReceiver { if(intent.getAction().equals(ACTION_CMD)) { executer.exec(text.toString(), intent.getBooleanExtra(SHOW_CONTENT, true)); - } else { + } else if(intent.getAction().equals(ACTION_OUTPUT)) { int color = intent.getIntExtra(COLOR, Integer.MAX_VALUE); + Object singleClickExtraObject, longClickExtraObject; + + singleClickExtraObject = intent.getStringExtra(ACTION); + longClickExtraObject = intent.getStringExtra(LONG_ACTION); + + if(singleClickExtraObject == null) singleClickExtraObject = intent.getParcelableExtra(ACTION); + if(longClickExtraObject == null) longClickExtraObject = intent.getParcelableExtra(LONG_ACTION); + + if(singleClickExtraObject != null || longClickExtraObject != null) { + text = new SpannableStringBuilder(text); + ((SpannableStringBuilder) text).setSpan(new LongClickableSpan(singleClickExtraObject, longClickExtraObject), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if(color != Integer.MAX_VALUE) { outputable.onOutput(color, text); } else { int type = intent.getIntExtra(TYPE, -1); if(type != -1) outputable.onOutput(text, type); - else outputable.onOutput(text, TerminalManager.CATEGORY_GENERAL); + else outputable.onOutput(text, TerminalManager.CATEGORY_OUTPUT); } + } else if(intent.getAction().equals(ACTION_INPUT)) { + inputable.in(text.toString()); } } else { String cmd = remoteInput.getString(TEXT); executer.exec(cmd, true); - int was = intent.getIntExtra(WAS_KEY, 0); - if(was == WAS_KEEPER_SERVICE) { - NotificationManagerCompat.from(context).notify(KeeperService.ONGOING_NOTIFICATION_ID, KeeperService.buildNotification(context)); - } else if(was == WAS_MUSIC_SERVICE) { -// do nothing - } +// int was = intent.getIntExtra(WAS_KEY, 0); +// if(was == WAS_KEEPER_SERVICE) { +// NotificationManagerCompat.from(context).notify(KeeperService.ONGOING_NOTIFICATION_ID, KeeperService.buildNotification(context)); +// } else if(was == WAS_MUSIC_SERVICE) { +//// do nothing +// } } } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/KeeperService.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/KeeperService.java deleted file mode 100755 index 94070c7..0000000 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/KeeperService.java +++ /dev/null @@ -1,72 +0,0 @@ -package ohi.andre.consolelauncher.tuils; - -import android.app.Notification; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.IBinder; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.RemoteInput; - -import ohi.andre.consolelauncher.LauncherActivity; -import ohi.andre.consolelauncher.R; - -public class KeeperService extends Service { - - public static final int ONGOING_NOTIFICATION_ID = 1001; - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - startForeground(ONGOING_NOTIFICATION_ID, buildNotification(getApplicationContext())); - return super.onStartCommand(intent, flags, startId); - } - - @Override - public boolean onUnbind(Intent intent) { - return true; - } - - public static Notification buildNotification(Context c) { - Intent resultIntent = new Intent(c, LauncherActivity.class); - PendingIntent resultPendingIntent = PendingIntent.getActivity( - c, - 0, - resultIntent, - PendingIntent.FLAG_UPDATE_CURRENT - ); - - NotificationCompat.Builder builder = new NotificationCompat.Builder(c) - .setSmallIcon(R.mipmap.ic_launcher) - .setTicker(c.getString(R.string.start_notification)) - .setWhen(System.currentTimeMillis()) - .setContentTitle(c.getString(R.string.app_name)) - .setContentText(c.getString(R.string.tui_running)) - .setContentIntent(resultPendingIntent); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - String label = "cmd"; - RemoteInput remoteInput = new RemoteInput.Builder(InputOutputReceiver.TEXT) - .setLabel(label) - .build(); - - Intent i = new Intent(InputOutputReceiver.ACTION_CMD); - i.putExtra(InputOutputReceiver.WAS_KEY, InputOutputReceiver.WAS_KEEPER_SERVICE); - - NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, label, - PendingIntent.getBroadcast(c.getApplicationContext(), 40, i, PendingIntent.FLAG_UPDATE_CURRENT)) - .addRemoteInput(remoteInput) - .build(); - - builder.addAction(action); - } - - return builder.build(); - } -} diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickMovementMethod.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickMovementMethod.java new file mode 100644 index 0000000..0f5446b --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickMovementMethod.java @@ -0,0 +1,139 @@ +package ohi.andre.consolelauncher.tuils; + +import android.text.Layout; +import android.text.Spannable; +import android.text.method.LinkMovementMethod; +import android.text.method.MovementMethod; +import android.view.MotionEvent; +import android.widget.TextView; + +/** + * Created by francescoandreuzzi on 17/11/2017. + */ + +public class LongClickMovementMethod extends LinkMovementMethod { + +// private Long lastClickTime = 0l; +// private int lastX = 0; +// private int lastY = 0; + + private int longClickDuration, lastLine = -1; + + private abstract class WasActivatedRunnable implements Runnable { + + public boolean wasActivated = false; + + @Override + public void run() { + wasActivated = true; + } + }; + + private WasActivatedRunnable runnable; + + @Override + public boolean onTouchEvent(final TextView widget, Spannable buffer, MotionEvent event) { + int action = event.getAction(); +// Tuils.log("action", action); + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_CANCEL) { + int x = (int) event.getX(); + int y = (int) event.getY(); +// lastX = x; +// lastY = y; +// int deltaX = Math.abs(x-lastX); +// int deltaY = Math.abs(y-lastY); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + final int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + final LongClickableSpan[] link = buffer.getSpans(off, off, LongClickableSpan.class); + +// Tuils.log("lastline", lastLine); +// Tuils.log("line", line); + if (action == MotionEvent.ACTION_UP) { +// Tuils.log("action up"); + +// if (System.currentTimeMillis() - lastClickTime < longClickDuration) { +// link[0].onClick(widget); +// } +// else if (deltaX < 10 && deltaY < 10) { +// link[0].onLongClick(widget); +// } + + if(runnable != null) { +// long click, do nothing + if(runnable.wasActivated) {} +// single click + else { + widget.removeCallbacks(runnable); + if(link.length > 0) link[0].onClick(widget); + } + + runnable = null; + } + + } else if (action == MotionEvent.ACTION_DOWN) { + +// Tuils.log("action down"); + +// Selection.setSelection(buffer, +// buffer.getSpanStart(link[0]), +// buffer.getSpanEnd(link[0])); + +// lastClickTime = System.currentTimeMillis(); + + if(link.length > 0) { + final LongClickableSpan span = link[0]; + runnable = new WasActivatedRunnable() { + + @Override + public void run() { + super.run(); + span.onLongClick(widget); + } + }; + } + + widget.postDelayed(runnable, longClickDuration); + } else { +// Tuils.log("action move or cancel"); + +// action_move + if(line != lastLine) { +// Tuils.log("line != last line"); + widget.removeCallbacks(runnable); + } + } + + lastLine = line; +// Tuils.log("updated kast line", lastLine); +// Tuils.log("#####"); + + return true; + } + + return super.onTouchEvent(widget, buffer, event); + } + + private static LongClickMovementMethod sInstance; + public static MovementMethod getInstance(int longClickDuration) { + if (sInstance == null) { + sInstance = new LongClickMovementMethod(); + sInstance.longClickDuration = longClickDuration; + } + + return sInstance; + } + + public static MovementMethod getInstance() { + return getInstance(-1); + } +} \ No newline at end of file diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickableSpan.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickableSpan.java new file mode 100644 index 0000000..398db4c --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/LongClickableSpan.java @@ -0,0 +1,125 @@ +package ohi.andre.consolelauncher.tuils; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Vibrator; +import android.text.TextPaint; +import android.text.style.ClickableSpan; +import android.view.MenuItem; +import android.view.View; +import android.widget.PopupMenu; + +import ohi.andre.consolelauncher.R; +import ohi.andre.consolelauncher.managers.notifications.NotificationManager; +import ohi.andre.consolelauncher.managers.notifications.NotificationService; + +/** + * Created by francescoandreuzzi on 22/10/2017. + */ + +public class LongClickableSpan extends ClickableSpan { + + public static int longPressVibrateDuration = -1; + + private Object clickO, longClickO; + private String longIntentKey; + + public LongClickableSpan(Object clickAction, Object longClickAction) { + this.clickO = clickAction; + this.longClickO = longClickAction; + this.longIntentKey = null; + } + + public LongClickableSpan(Object clickAction) { + this.clickO = clickAction; + this.longClickO = null; + this.longIntentKey = null; + } + + public LongClickableSpan(Object clickAction, Object longClickAction, String longIntentKey) { + this.clickO = clickAction; + this.longClickO = longClickAction; + this.longIntentKey = longIntentKey; + } + + public LongClickableSpan(Object clickAction, String longIntentKey) { + this.clickO = clickAction; + this.longClickO = null; + this.longIntentKey = longIntentKey; + } + + public LongClickableSpan(String longIntentKey) { + this.clickO = null; + this.longClickO = null; + this.longIntentKey = longIntentKey; + } + + @Override + public void updateDrawState(TextPaint ds) {} + + @Override + public void onClick(View widget) { + execute(widget, clickO); + } + + public void onLongClick(View widget) { + if(execute(widget, longClickO, longIntentKey) && longPressVibrateDuration > 0) ((Vibrator) widget.getContext().getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE)).vibrate(longPressVibrateDuration); + } + + private static boolean execute(View v, Object o) { + return execute(v, o, null); + } + + private static boolean execute(View v, Object o, String intentKey) { + if(o == null) return false; + + if(o instanceof String) { + Intent intent = new Intent(intentKey != null ? intentKey : InputOutputReceiver.ACTION_CMD); + intent.putExtra(InputOutputReceiver.SHOW_CONTENT, false); + intent.putExtra(InputOutputReceiver.TEXT, (String) o); + v.getContext().sendBroadcast(intent); + } else if(o instanceof PendingIntent) { + PendingIntent pi = (PendingIntent) o; + + try { + pi.send(); + } catch (PendingIntent.CanceledException e) { + Tuils.log(e); + } + } else if(o instanceof NotificationService.Notification) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) { + PopupMenu menu = new PopupMenu(v.getContext().getApplicationContext(), v); + menu.getMenuInflater().inflate(R.menu.notification_menu, menu.getMenu()); + + final NotificationService.Notification n = (NotificationService.Notification) o; + + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + + @Override + public boolean onMenuItemClick(MenuItem item) { + int id = item.getItemId(); + + switch (id) { + case R.id.exclude_app: + NotificationManager.setState(n.pkg, false); + break; + case R.id.exclude_notification: + NotificationManager.addFilter(n.text, -1); + break; + default: + return false; + } + + return true; + } + }); + + menu.show(); + } + } + + return true; + } +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/StoppableThread.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/StoppableThread.java index f98c1ec..a4bfd9b 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/StoppableThread.java +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/StoppableThread.java @@ -1,7 +1,5 @@ package ohi.andre.consolelauncher.tuils; -import android.util.Log; - /** * Created by francescoandreuzzi on 27/04/2017. */ @@ -9,6 +7,16 @@ import android.util.Log; public class StoppableThread extends Thread { private volatile boolean stopped = false; + public StoppableThread() { + Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + Tuils.log(e); + Tuils.toFile(e); + System.exit(1); + } + }); + } @Override public void interrupt() { diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java index 4836734..dcbdf10 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java @@ -13,14 +13,19 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.Typeface; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.BatteryManager; import android.os.Build; import android.os.Environment; +import android.os.Parcelable; import android.os.Process; import android.os.StatFs; import android.provider.Settings; +import android.telephony.TelephonyManager; import android.text.SpannableString; import android.text.Spanned; import android.text.TextUtils; @@ -33,6 +38,8 @@ import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.webkit.MimeTypeMap; +import android.widget.EditText; +import android.widget.TextView; import java.io.BufferedReader; import java.io.File; @@ -44,10 +51,14 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.Reader; import java.io.StringWriter; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.RoundingMode; +import java.net.HttpURLConnection; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -58,6 +69,7 @@ import java.util.regex.Pattern; import dalvik.system.DexFile; import ohi.andre.consolelauncher.BuildConfig; +import ohi.andre.consolelauncher.R; import ohi.andre.consolelauncher.managers.TerminalManager; import ohi.andre.consolelauncher.managers.music.MusicManager2; import ohi.andre.consolelauncher.managers.music.Song; @@ -138,6 +150,8 @@ public class Tuils { } public static boolean arrayContains(int[] array, int value) { + if(array == null) return false; + for(int i : array) { if(i == value) { return true; @@ -146,6 +160,55 @@ public class Tuils { return false; } + static final char CHAR_SPACE = ' '; + public static boolean equalsIgnoreSpaceAndCase(String s1, String s2) { + if (s1 == s2) { + return true; + } + + int i1 = 0, i2 = 0; + while(true) { + if(i1 == s1.length() || i2 == s2.length()) break; + + char c1 = Character.toLowerCase(s1.charAt(i1)); + char c2 = Character.toLowerCase(s2.charAt(i2)); + + if(c1 != CHAR_SPACE) { + if(c2 != CHAR_SPACE) { + if(c1 != c2) return false; + } else { +// c1 is not space, c2 is space +// i1 remains, i2 changes + i2++; + continue; + } + } + +// c1 is space + if(c2 == CHAR_SPACE) { +// c1 is space, c2 is space + i1++;i2++; + } else { +// c1 is space, c2 is not space + i1++; + continue; + } + } + + return true; + } + + public static String readerToString(Reader initialReader) throws IOException { + char[] arr = new char[8 * 1024]; + StringBuilder buffer = new StringBuilder(); + int numCharsRead; + while ((numCharsRead = initialReader.read(arr, 0, arr.length)) != -1) { + buffer.append(arr, 0, numCharsRead); + } + initialReader.close(); + return buffer.toString(); + } + public static void registerBatteryReceiver(Context context, OnBatteryUpdate listener) { try { IntentFilter iFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); @@ -218,19 +281,24 @@ public class Tuils { return songs; } - public static void download(InputStream in, File file) throws Exception { + public static long download(InputStream in, File file) throws Exception { OutputStream out = new FileOutputStream(file, false); byte data[] = new byte[1024]; + long bytes = 0; + int count; while ((count = in.read(data)) != -1) { out.write(data, 0, count); + bytes += count; } out.flush(); out.close(); in.close(); + + return bytes; } public static void write(File file, String separator, String... ss) throws Exception { @@ -384,7 +452,9 @@ public class Tuils { public static SpannableString span(Context context, String text, int color, int size) { SpannableString spannableString = new SpannableString(text); if(size != Integer.MAX_VALUE && context != null) spannableString.setSpan(new AbsoluteSizeSpan(convertSpToPixels(size, context)), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - if(color != Integer.MAX_VALUE) spannableString.setSpan(new ForegroundColorSpan(color), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + if(color != Integer.MAX_VALUE) { + spannableString.setSpan(new ForegroundColorSpan(color), 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } return spannableString; } @@ -484,7 +554,7 @@ public class Tuils { } public static void sendOutput(int color, Context context, CharSequence s) { - sendOutput(color, context, s, TerminalManager.CATEGORY_GENERAL); + sendOutput(color, context, s, TerminalManager.CATEGORY_OUTPUT); } public static void sendOutput(Context context, CharSequence s, int type) { @@ -499,6 +569,47 @@ public class Tuils { context.sendBroadcast(intent); } + public static void sendOutput(Context context, CharSequence s, int type, Object action) { + sendOutput(Integer.MAX_VALUE, context, s, type, action); + } + + public static void sendOutput(int color, Context context, CharSequence s, int type, Object action) { + Intent intent = new Intent(InputOutputReceiver.ACTION_OUTPUT); + intent.putExtra(InputOutputReceiver.TEXT, s); + intent.putExtra(InputOutputReceiver.COLOR, color); + intent.putExtra(InputOutputReceiver.TYPE, type); + + if(action instanceof String) intent.putExtra(InputOutputReceiver.ACTION, (String) action); + else if(action instanceof Parcelable) intent.putExtra(InputOutputReceiver.ACTION, (Parcelable) action); + + context.sendBroadcast(intent); + } + + public static void sendOutput(Context context, CharSequence s, int type, Object action, Object longAction) { + sendOutput(Integer.MAX_VALUE, context, s, type, action, longAction); + } + + public static void sendOutput(int color, Context context, CharSequence s, int type, Object action, Object longAction) { + Intent intent = new Intent(InputOutputReceiver.ACTION_OUTPUT); + intent.putExtra(InputOutputReceiver.TEXT, s); + intent.putExtra(InputOutputReceiver.COLOR, color); + intent.putExtra(InputOutputReceiver.TYPE, type); + + if(action instanceof String) intent.putExtra(InputOutputReceiver.ACTION, (String) action); + else if(action instanceof Parcelable) intent.putExtra(InputOutputReceiver.ACTION, (Parcelable) action); + + if(longAction instanceof String) intent.putExtra(InputOutputReceiver.LONG_ACTION, (String) longAction); + else if(longAction instanceof Parcelable) intent.putExtra(InputOutputReceiver.LONG_ACTION, (Parcelable) longAction); + + context.sendBroadcast(intent); + } + + public static void sendInput(Context context, String text) { + Intent intent = new Intent(InputOutputReceiver.ACTION_INPUT); + intent.putExtra(InputOutputReceiver.TEXT, text); + context.sendBroadcast(intent); + } + public static final int TERA = 0; public static final int GIGA = 1; public static final int MEGA = 2; @@ -563,6 +674,8 @@ public class Tuils { } private static String getNicePath(String filePath) { + if(filePath == null) return "null"; + String home = XMLPrefsManager.get(File.class, Behavior.home_path).getAbsolutePath(); if(filePath.equals(home)) { @@ -704,11 +817,30 @@ public class Tuils { if(o instanceof Throwable) { Log.e("andre", "", (Throwable) o); + } else if(o instanceof Object[]){ + Log.e("andre", Arrays.toString((Object[]) o)); } else { Log.e("andre", String.valueOf(o)); } } + public static void log(Object o, Object o2) { + if(o instanceof Object[] && o2 instanceof Object[]){ + Log.e("andre", Arrays.toString((Object[]) o) + " -- " + Arrays.toString((Object[]) o2)); + } else { + Log.e("andre", String.valueOf(o) + " -- " + String.valueOf(o2)); + } + } + + public static boolean hasInternetAccess() { + try { + HttpURLConnection urlc = (HttpURLConnection) (new URL("http://clients3.google.com/generate_204").openConnection()); + return (urlc.getResponseCode() == 204 && urlc.getContentLength() == 0); + } catch (IOException e) { + return false; + } + } + public static <T> T getDefaultValue(Class<T> clazz) { return (T) Array.get(Array.newInstance(clazz, 1), 0); } @@ -807,7 +939,7 @@ public class Tuils { return true; } -// return -1 if only digit +// return 0 if only digit public static char firstNonDigit(String s) { if(s == null) { return 0; @@ -994,4 +1126,75 @@ public class Tuils { public static String removeSpaces(String string) { return string.replaceAll(SPACE_REGEXP, EMPTYSTRING); } + + public static String getNetworkType(Context context) { + TelephonyManager mTelephonyManager = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + int networkType = mTelephonyManager.getNetworkType(); + switch (networkType) { + case TelephonyManager.NETWORK_TYPE_GPRS: + case TelephonyManager.NETWORK_TYPE_EDGE: + case TelephonyManager.NETWORK_TYPE_CDMA: + case TelephonyManager.NETWORK_TYPE_1xRTT: + case TelephonyManager.NETWORK_TYPE_IDEN: + return "2g"; + case TelephonyManager.NETWORK_TYPE_UMTS: + case TelephonyManager.NETWORK_TYPE_EVDO_0: + case TelephonyManager.NETWORK_TYPE_EVDO_A: + case TelephonyManager.NETWORK_TYPE_HSDPA: + case TelephonyManager.NETWORK_TYPE_HSUPA: + case TelephonyManager.NETWORK_TYPE_HSPA: + case TelephonyManager.NETWORK_TYPE_EVDO_B: + case TelephonyManager.NETWORK_TYPE_EHRPD: + case TelephonyManager.NETWORK_TYPE_HSPAP: + return "3g"; + case TelephonyManager.NETWORK_TYPE_LTE: + return "4g"; + default: + return "unknown"; + } + } + + public static void setCursorDrawableColor(EditText editText, int color) { + try { + Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); + fCursorDrawableRes.setAccessible(true); + int mCursorDrawableRes = fCursorDrawableRes.getInt(editText); + Field fEditor = TextView.class.getDeclaredField("mEditor"); + fEditor.setAccessible(true); + Object editor = fEditor.get(editText); + Class<?> clazz = editor.getClass(); + Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable"); + fCursorDrawable.setAccessible(true); + Drawable[] drawables = new Drawable[2]; + drawables[0] = editText.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[1] = editText.getContext().getResources().getDrawable(mCursorDrawableRes); + drawables[0].setColorFilter(color, PorterDuff.Mode.SRC_IN); + drawables[1].setColorFilter(color, PorterDuff.Mode.SRC_IN); + fCursorDrawable.set(editor, drawables); + } catch (Throwable ignored) {} + } + + public static int nOfBytes(File file) { + int count = 0; + try { + FileInputStream in = new FileInputStream(file); + + while(in.read() != -1) count++; + + return count; + } catch (IOException e) { + Tuils.log(e); + return count; + } + } + + public static void sendXMLParseError(Context context, String PATH, Exception e) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + PATH + context.getString(R.string.output_xmlproblem2) + + Tuils.NEWLINE + context.getString(R.string.output_errorlabel) + e.toString()); + } + + public static void sendXMLParseError(Context context, String PATH) { + Tuils.sendOutput(Color.RED, context, context.getString(R.string.output_xmlproblem1) + Tuils.SPACE + PATH + context.getString(R.string.output_xmlproblem2)); + } } diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html4EscapeSymbolsInitializer.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html4EscapeSymbolsInitializer.java new file mode 100755 index 0000000..d4f774a --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html4EscapeSymbolsInitializer.java @@ -0,0 +1,356 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +import java.util.Arrays; + +/** + * <p> + * This class initializes the {@link org.unbescape.html.HtmlEscapeSymbols#HTML4_SYMBOLS} structure. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +final class Html4EscapeSymbolsInitializer { + + + static HtmlEscapeSymbols initializeHtml4() { + + final HtmlEscapeSymbols.References html4References = new HtmlEscapeSymbols.References(); + + /* + * ----------------------------------------------------------------- + * HTML4 NAMED CHARACTER REFERENCES (CHARACTER ENTITY REFERENCES) + * See: http://www.w3.org/TR/html4/sgml/entities.html + * ----------------------------------------------------------------- + */ + + /* HTML NCRs FOR MARKUP-SIGNIFICANT CHARACTERS */ + // (Note HTML 4 does not include ' as a valid NCR) + html4References.addReference('"', """); + html4References.addReference('&', "&"); + html4References.addReference('<', "<"); + html4References.addReference('>', ">"); + /* HTML NCRs FOR ISO-8859-1 CHARACTERS */ + html4References.addReference('\u00A0', " "); + html4References.addReference('\u00A1', "¡"); + html4References.addReference('\u00A2', "¢"); + html4References.addReference('\u00A3', "£"); + html4References.addReference('\u00A4', "¤"); + html4References.addReference('\u00A5', "¥"); + html4References.addReference('\u00A6', "¦"); + html4References.addReference('\u00A7', "§"); + html4References.addReference('\u00A8', "¨"); + html4References.addReference('\u00A9', "©"); + html4References.addReference('\u00AA', "ª"); + html4References.addReference('\u00AB', "«"); + html4References.addReference('\u00AC', "¬"); + html4References.addReference('\u00AD', "­"); + html4References.addReference('\u00AE', "®"); + html4References.addReference('\u00AF', "¯"); + html4References.addReference('\u00B0', "°"); + html4References.addReference('\u00B1', "±"); + html4References.addReference('\u00B2', "²"); + html4References.addReference('\u00B3', "³"); + html4References.addReference('\u00B4', "´"); + html4References.addReference('\u00B5', "µ"); + html4References.addReference('\u00B6', "¶"); + html4References.addReference('\u00B7', "·"); + html4References.addReference('\u00B8', "¸"); + html4References.addReference('\u00B9', "¹"); + html4References.addReference('\u00BA', "º"); + html4References.addReference('\u00BB', "»"); + html4References.addReference('\u00BC', "¼"); + html4References.addReference('\u00BD', "½"); + html4References.addReference('\u00BE', "¾"); + html4References.addReference('\u00BF', "¿"); + html4References.addReference('\u00C0', "À"); + html4References.addReference('\u00C1', "Á"); + html4References.addReference('\u00C2', "Â"); + html4References.addReference('\u00C3', "Ã"); + html4References.addReference('\u00C4', "Ä"); + html4References.addReference('\u00C5', "Å"); + html4References.addReference('\u00C6', "Æ"); + html4References.addReference('\u00C7', "Ç"); + html4References.addReference('\u00C8', "È"); + html4References.addReference('\u00C9', "É"); + html4References.addReference('\u00CA', "Ê"); + html4References.addReference('\u00CB', "Ë"); + html4References.addReference('\u00CC', "Ì"); + html4References.addReference('\u00CD', "Í"); + html4References.addReference('\u00CE', "Î"); + html4References.addReference('\u00CF', "Ï"); + html4References.addReference('\u00D0', "Ð"); + html4References.addReference('\u00D1', "Ñ"); + html4References.addReference('\u00D2', "Ò"); + html4References.addReference('\u00D3', "Ó"); + html4References.addReference('\u00D4', "Ô"); + html4References.addReference('\u00D5', "Õ"); + html4References.addReference('\u00D6', "Ö"); + html4References.addReference('\u00D7', "×"); + html4References.addReference('\u00D8', "Ø"); + html4References.addReference('\u00D9', "Ù"); + html4References.addReference('\u00DA', "Ú"); + html4References.addReference('\u00DB', "Û"); + html4References.addReference('\u00DC', "Ü"); + html4References.addReference('\u00DD', "Ý"); + html4References.addReference('\u00DE', "Þ"); + html4References.addReference('\u00DF', "ß"); + html4References.addReference('\u00E0', "à"); + html4References.addReference('\u00E1', "á"); + html4References.addReference('\u00E2', "â"); + html4References.addReference('\u00E3', "ã"); + html4References.addReference('\u00E4', "ä"); + html4References.addReference('\u00E5', "å"); + html4References.addReference('\u00E6', "æ"); + html4References.addReference('\u00E7', "ç"); + html4References.addReference('\u00E8', "è"); + html4References.addReference('\u00E9', "é"); + html4References.addReference('\u00EA', "ê"); + html4References.addReference('\u00EB', "ë"); + html4References.addReference('\u00EC', "ì"); + html4References.addReference('\u00ED', "í"); + html4References.addReference('\u00EE', "î"); + html4References.addReference('\u00EF', "ï"); + html4References.addReference('\u00F0', "ð"); + html4References.addReference('\u00F1', "ñ"); + html4References.addReference('\u00F2', "ò"); + html4References.addReference('\u00F3', "ó"); + html4References.addReference('\u00F4', "ô"); + html4References.addReference('\u00F5', "õ"); + html4References.addReference('\u00F6', "ö"); + html4References.addReference('\u00F7', "÷"); + html4References.addReference('\u00F8', "ø"); + html4References.addReference('\u00F9', "ù"); + html4References.addReference('\u00FA', "ú"); + html4References.addReference('\u00FB', "û"); + html4References.addReference('\u00FC', "ü"); + html4References.addReference('\u00FD', "ý"); + html4References.addReference('\u00FE', "þ"); + html4References.addReference('\u00FF', "ÿ"); + /* HTML NCRs FOR SYMBOLS, MATHEMATICAL SYMBOLS AND GREEK LETTERS */ + /* - Greek */ + html4References.addReference('\u0192', "ƒ"); + html4References.addReference('\u0391', "Α"); + html4References.addReference('\u0392', "Β"); + html4References.addReference('\u0393', "Γ"); + html4References.addReference('\u0394', "Δ"); + html4References.addReference('\u0395', "Ε"); + html4References.addReference('\u0396', "Ζ"); + html4References.addReference('\u0397', "Η"); + html4References.addReference('\u0398', "Θ"); + html4References.addReference('\u0399', "Ι"); + html4References.addReference('\u039A', "Κ"); + html4References.addReference('\u039B', "Λ"); + html4References.addReference('\u039C', "Μ"); + html4References.addReference('\u039D', "Ν"); + html4References.addReference('\u039E', "Ξ"); + html4References.addReference('\u039F', "Ο"); + html4References.addReference('\u03A0', "Π"); + html4References.addReference('\u03A1', "Ρ"); + html4References.addReference('\u03A3', "Σ"); + html4References.addReference('\u03A4', "Τ"); + html4References.addReference('\u03A5', "Υ"); + html4References.addReference('\u03A6', "Φ"); + html4References.addReference('\u03A7', "Χ"); + html4References.addReference('\u03A8', "Ψ"); + html4References.addReference('\u03A9', "Ω"); + html4References.addReference('\u03B1', "α"); + html4References.addReference('\u03B2', "β"); + html4References.addReference('\u03B3', "γ"); + html4References.addReference('\u03B4', "δ"); + html4References.addReference('\u03B5', "ε"); + html4References.addReference('\u03B6', "ζ"); + html4References.addReference('\u03B7', "η"); + html4References.addReference('\u03B8', "θ"); + html4References.addReference('\u03B9', "ι"); + html4References.addReference('\u03BA', "κ"); + html4References.addReference('\u03BB', "λ"); + html4References.addReference('\u03BC', "μ"); + html4References.addReference('\u03BD', "ν"); + html4References.addReference('\u03BE', "ξ"); + html4References.addReference('\u03BF', "ο"); + html4References.addReference('\u03C0', "π"); + html4References.addReference('\u03C1', "ρ"); + html4References.addReference('\u03C2', "ς"); + html4References.addReference('\u03C3', "σ"); + html4References.addReference('\u03C4', "τ"); + html4References.addReference('\u03C5', "υ"); + html4References.addReference('\u03C6', "φ"); + html4References.addReference('\u03C7', "χ"); + html4References.addReference('\u03C8', "ψ"); + html4References.addReference('\u03C9', "ω"); + html4References.addReference('\u03D1', "ϑ"); + html4References.addReference('\u03D2', "ϒ"); + html4References.addReference('\u03D6', "ϖ"); + /* - General punctuation */ + html4References.addReference('\u2022', "•"); + html4References.addReference('\u2026', "…"); + html4References.addReference('\u2032', "′"); + html4References.addReference('\u2033', "″"); + html4References.addReference('\u203E', "‾"); + html4References.addReference('\u2044', "⁄"); + /* - Letter-like symbols */ + html4References.addReference('\u2118', "℘"); + html4References.addReference('\u2111', "ℑ"); + html4References.addReference('\u211C', "ℜ"); + html4References.addReference('\u2122', "™"); + html4References.addReference('\u2135', "ℵ"); + /* - Arrows */ + html4References.addReference('\u2190', "←"); + html4References.addReference('\u2191', "↑"); + html4References.addReference('\u2192', "→"); + html4References.addReference('\u2193', "↓"); + html4References.addReference('\u2194', "↔"); + html4References.addReference('\u21B5', "↵"); + html4References.addReference('\u21D0', "⇐"); + html4References.addReference('\u21D1', "⇑"); + html4References.addReference('\u21D2', "⇒"); + html4References.addReference('\u21D3', "⇓"); + html4References.addReference('\u21D4', "⇔"); + /* - Mathematical operators */ + html4References.addReference('\u2200', "∀"); + html4References.addReference('\u2202', "∂"); + html4References.addReference('\u2203', "∃"); + html4References.addReference('\u2205', "∅"); + html4References.addReference('\u2207', "∇"); + html4References.addReference('\u2208', "∈"); + html4References.addReference('\u2209', "∉"); + html4References.addReference('\u220B', "∋"); + html4References.addReference('\u220F', "∏"); + html4References.addReference('\u2211', "∑"); + html4References.addReference('\u2212', "−"); + html4References.addReference('\u2217', "∗"); + html4References.addReference('\u221A', "√"); + html4References.addReference('\u221D', "∝"); + html4References.addReference('\u221E', "∞"); + html4References.addReference('\u2220', "∠"); + html4References.addReference('\u2227', "∧"); + html4References.addReference('\u2228', "∨"); + html4References.addReference('\u2229', "∩"); + html4References.addReference('\u222A', "∪"); + html4References.addReference('\u222B', "∫"); + html4References.addReference('\u2234', "∴"); + html4References.addReference('\u223C', "∼"); + html4References.addReference('\u2245', "≅"); + html4References.addReference('\u2248', "≈"); + html4References.addReference('\u2260', "≠"); + html4References.addReference('\u2261', "≡"); + html4References.addReference('\u2264', "≤"); + html4References.addReference('\u2265', "≥"); + html4References.addReference('\u2282', "⊂"); + html4References.addReference('\u2283', "⊃"); + html4References.addReference('\u2284', "⊄"); + html4References.addReference('\u2286', "⊆"); + html4References.addReference('\u2287', "⊇"); + html4References.addReference('\u2295', "⊕"); + html4References.addReference('\u2297', "⊗"); + html4References.addReference('\u22A5', "⊥"); + html4References.addReference('\u22C5', "⋅"); + /* - Miscellaneous technical */ + html4References.addReference('\u2308', "⌈"); + html4References.addReference('\u2309', "⌉"); + html4References.addReference('\u230A', "⌊"); + html4References.addReference('\u230B', "⌋"); + html4References.addReference('\u2329', "⟨"); + html4References.addReference('\u232A', "⟩"); + /* - Geometric shapes */ + html4References.addReference('\u25CA', "◊"); + html4References.addReference('\u2660', "♠"); + html4References.addReference('\u2663', "♣"); + html4References.addReference('\u2665', "♥"); + html4References.addReference('\u2666', "♦"); + /* HTML NCRs FOR INTERNATIONALIZATION CHARACTERS */ + /* - Latin Extended-A */ + html4References.addReference('\u0152', "Œ"); + html4References.addReference('\u0153', "œ"); + html4References.addReference('\u0160', "Š"); + html4References.addReference('\u0161', "š"); + html4References.addReference('\u0178', "Ÿ"); + /* - Spacing modifier letters */ + html4References.addReference('\u02C6', "ˆ"); + html4References.addReference('\u02DC', "˜"); + /* - General punctuation */ + html4References.addReference('\u2002', " "); + html4References.addReference('\u2003', " "); + html4References.addReference('\u2009', " "); + html4References.addReference('\u200C', "‌"); + html4References.addReference('\u200D', "‍"); + html4References.addReference('\u200E', "‎"); + html4References.addReference('\u200F', "‏"); + html4References.addReference('\u2013', "–"); + html4References.addReference('\u2014', "—"); + html4References.addReference('\u2018', "‘"); + html4References.addReference('\u2019', "’"); + html4References.addReference('\u201A', "‚"); + html4References.addReference('\u201C', "“"); + html4References.addReference('\u201D', "”"); + html4References.addReference('\u201E', "„"); + html4References.addReference('\u2020', "†"); + html4References.addReference('\u2021', "‡"); + html4References.addReference('\u2030', "‰"); + html4References.addReference('\u2039', "‹"); + html4References.addReference('\u203A', "›"); + html4References.addReference('\u20AC', "€"); + + + /* + * Initialization of escape levels. + * Defined levels : + * + * - Level 0 : Only markup-significant characters except the apostrophe (') + * - Level 1 : Only markup-significant characters (including the apostrophe) + * - Level 2 : Markup-significant characters plus all non-ASCII + * - Level 3 : All non-alphanumeric characters + * - Level 4 : All characters + */ + final byte[] escapeLevels = new byte[0x7f + 2]; + Arrays.fill(escapeLevels, (byte)3); + for (char c = 'A'; c <= 'Z'; c++) { + escapeLevels[c] = 4; + } + for (char c = 'a'; c <= 'z'; c++) { + escapeLevels[c] = 4; + } + for (char c = '0'; c <= '9'; c++) { + escapeLevels[c] = 4; + } + escapeLevels['\''] = 1; + escapeLevels['"'] = 0; + escapeLevels['<'] = 0; + escapeLevels['>'] = 0; + escapeLevels['&'] = 0; + escapeLevels[0x7f + 1] = 2; + + + return new HtmlEscapeSymbols(html4References, escapeLevels); + + } + + + private Html4EscapeSymbolsInitializer() { + super(); + } + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html5EscapeSymbolsInitializer.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html5EscapeSymbolsInitializer.java new file mode 100755 index 0000000..b4cbf22 --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/Html5EscapeSymbolsInitializer.java @@ -0,0 +1,2320 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +import java.util.Arrays; + +/** + * <p> + * This class initializes the {@link org.unbescape.html.HtmlEscapeSymbols#HTML5_SYMBOLS} structure. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +final class Html5EscapeSymbolsInitializer { + + + static HtmlEscapeSymbols initializeHtml5() { + + final HtmlEscapeSymbols.References html5References = new HtmlEscapeSymbols.References(); + + /* + * -------------------------------------------------------------------------------------------------- + * HTML5 NAMED CHARACTER REFERENCES + * See: http://www.w3.org/TR/html5/syntax.html#named-character-references [HTML5] + * http://www.w3.org/TR/html51/syntax.html#named-character-references [HTML 5.1] + * -------------------------------------------------------------------------------------------------- + */ + html5References.addReference( 9, "	"); + html5References.addReference( 10, "
"); + html5References.addReference( 33, "!"); + html5References.addReference( 34, """); + html5References.addReference( 34, """); + html5References.addReference( 34, """); + html5References.addReference( 34, """); + html5References.addReference( 35, "#"); + html5References.addReference( 36, "$"); + html5References.addReference( 37, "%"); + html5References.addReference( 38, "&"); + html5References.addReference( 38, "&"); + html5References.addReference( 38, "&"); + html5References.addReference( 38, "&"); + html5References.addReference( 39, "'"); + html5References.addReference( 40, "("); + html5References.addReference( 41, ")"); + html5References.addReference( 42, "*"); + html5References.addReference( 42, "*"); + html5References.addReference( 43, "+"); + html5References.addReference( 44, ","); + html5References.addReference( 46, "."); + html5References.addReference( 47, "/"); + html5References.addReference( 58, ":"); + html5References.addReference( 59, ";"); + html5References.addReference( 60, "<"); + html5References.addReference( 60, "<"); + html5References.addReference( 60, "<"); + html5References.addReference( 60, "<"); + html5References.addReference( 60, 8402, "<⃒"); + html5References.addReference( 61, "="); + html5References.addReference( 61, 8421, "=⃥"); + html5References.addReference( 62, ">"); + html5References.addReference( 62, ">"); + html5References.addReference( 62, ">"); + html5References.addReference( 62, ">"); + html5References.addReference( 62, 8402, ">⃒"); + html5References.addReference( 63, "?"); + html5References.addReference( 64, "@"); + html5References.addReference( 91, "["); + html5References.addReference( 91, "["); + html5References.addReference( 92, "\"); + html5References.addReference( 93, "]"); + html5References.addReference( 93, "]"); + html5References.addReference( 94, "^"); + html5References.addReference( 95, "_"); + html5References.addReference( 95, "_"); + html5References.addReference( 96, "`"); + html5References.addReference( 96, "`"); + html5References.addReference( 102, 106, "fj"); + html5References.addReference( 123, "{"); + html5References.addReference( 123, "{"); + html5References.addReference( 124, "|"); + html5References.addReference( 124, "|"); + html5References.addReference( 124, "|"); + html5References.addReference( 125, "}"); + html5References.addReference( 125, "}"); + html5References.addReference( 160, " "); + html5References.addReference( 160, " "); + html5References.addReference( 160, " "); + html5References.addReference( 161, "¡"); + html5References.addReference( 161, "¡"); + html5References.addReference( 162, "¢"); + html5References.addReference( 162, "¢"); + html5References.addReference( 163, "£"); + html5References.addReference( 163, "£"); + html5References.addReference( 164, "¤"); + html5References.addReference( 164, "¤"); + html5References.addReference( 165, "¥"); + html5References.addReference( 165, "¥"); + html5References.addReference( 166, "¦"); + html5References.addReference( 166, "¦"); + html5References.addReference( 167, "§"); + html5References.addReference( 167, "§"); + html5References.addReference( 168, "¨"); + html5References.addReference( 168, "¨"); + html5References.addReference( 168, "¨"); + html5References.addReference( 168, "¨"); + html5References.addReference( 168, "¨"); + html5References.addReference( 169, "©"); + html5References.addReference( 169, "©"); + html5References.addReference( 169, "©"); + html5References.addReference( 169, "©"); + html5References.addReference( 170, "ª"); + html5References.addReference( 170, "ª"); + html5References.addReference( 171, "«"); + html5References.addReference( 171, "«"); + html5References.addReference( 172, "¬"); + html5References.addReference( 172, "¬"); + html5References.addReference( 173, "­"); + html5References.addReference( 173, "­"); + html5References.addReference( 174, "®"); + html5References.addReference( 174, "®"); + html5References.addReference( 174, "®"); + html5References.addReference( 174, "®"); + html5References.addReference( 174, "®"); + html5References.addReference( 175, "¯"); + html5References.addReference( 175, "¯"); + html5References.addReference( 175, "¯"); + html5References.addReference( 176, "°"); + html5References.addReference( 176, "°"); + html5References.addReference( 177, "±"); + html5References.addReference( 177, "±"); + html5References.addReference( 177, "±"); + html5References.addReference( 177, "±"); + html5References.addReference( 178, "²"); + html5References.addReference( 178, "²"); + html5References.addReference( 179, "³"); + html5References.addReference( 179, "³"); + html5References.addReference( 180, "´"); + html5References.addReference( 180, "´"); + html5References.addReference( 180, "´"); + html5References.addReference( 181, "µ"); + html5References.addReference( 181, "µ"); + html5References.addReference( 182, "¶"); + html5References.addReference( 182, "¶"); + html5References.addReference( 183, "·"); + html5References.addReference( 183, "·"); + html5References.addReference( 183, "·"); + html5References.addReference( 183, "·"); + html5References.addReference( 184, "¸"); + html5References.addReference( 184, "¸"); + html5References.addReference( 184, "¸"); + html5References.addReference( 185, "¹"); + html5References.addReference( 185, "¹"); + html5References.addReference( 186, "º"); + html5References.addReference( 186, "º"); + html5References.addReference( 187, "»"); + html5References.addReference( 187, "»"); + html5References.addReference( 188, "¼"); + html5References.addReference( 188, "¼"); + html5References.addReference( 189, "½"); + html5References.addReference( 189, "½"); + html5References.addReference( 189, "½"); + html5References.addReference( 190, "¾"); + html5References.addReference( 190, "¾"); + html5References.addReference( 191, "¿"); + html5References.addReference( 191, "¿"); + html5References.addReference( 192, "À"); + html5References.addReference( 192, "À"); + html5References.addReference( 193, "Á"); + html5References.addReference( 193, "Á"); + html5References.addReference( 194, "Â"); + html5References.addReference( 194, "Â"); + html5References.addReference( 195, "Ã"); + html5References.addReference( 195, "Ã"); + html5References.addReference( 196, "Ä"); + html5References.addReference( 196, "Ä"); + html5References.addReference( 197, "Å"); + html5References.addReference( 197, "Å"); + html5References.addReference( 197, "Å"); + html5References.addReference( 198, "Æ"); + html5References.addReference( 198, "Æ"); + html5References.addReference( 199, "Ç"); + html5References.addReference( 199, "Ç"); + html5References.addReference( 200, "È"); + html5References.addReference( 200, "È"); + html5References.addReference( 201, "É"); + html5References.addReference( 201, "É"); + html5References.addReference( 202, "Ê"); + html5References.addReference( 202, "Ê"); + html5References.addReference( 203, "Ë"); + html5References.addReference( 203, "Ë"); + html5References.addReference( 204, "Ì"); + html5References.addReference( 204, "Ì"); + html5References.addReference( 205, "Í"); + html5References.addReference( 205, "Í"); + html5References.addReference( 206, "Î"); + html5References.addReference( 206, "Î"); + html5References.addReference( 207, "Ï"); + html5References.addReference( 207, "Ï"); + html5References.addReference( 208, "Ð"); + html5References.addReference( 208, "Ð"); + html5References.addReference( 209, "Ñ"); + html5References.addReference( 209, "Ñ"); + html5References.addReference( 210, "Ò"); + html5References.addReference( 210, "Ò"); + html5References.addReference( 211, "Ó"); + html5References.addReference( 211, "Ó"); + html5References.addReference( 212, "Ô"); + html5References.addReference( 212, "Ô"); + html5References.addReference( 213, "Õ"); + html5References.addReference( 213, "Õ"); + html5References.addReference( 214, "Ö"); + html5References.addReference( 214, "Ö"); + html5References.addReference( 215, "×"); + html5References.addReference( 215, "×"); + html5References.addReference( 216, "Ø"); + html5References.addReference( 216, "Ø"); + html5References.addReference( 217, "Ù"); + html5References.addReference( 217, "Ù"); + html5References.addReference( 218, "Ú"); + html5References.addReference( 218, "Ú"); + html5References.addReference( 219, "Û"); + html5References.addReference( 219, "Û"); + html5References.addReference( 220, "Ü"); + html5References.addReference( 220, "Ü"); + html5References.addReference( 221, "Ý"); + html5References.addReference( 221, "Ý"); + html5References.addReference( 222, "Þ"); + html5References.addReference( 222, "Þ"); + html5References.addReference( 223, "ß"); + html5References.addReference( 223, "ß"); + html5References.addReference( 224, "à"); + html5References.addReference( 224, "à"); + html5References.addReference( 225, "á"); + html5References.addReference( 225, "á"); + html5References.addReference( 226, "â"); + html5References.addReference( 226, "â"); + html5References.addReference( 227, "ã"); + html5References.addReference( 227, "ã"); + html5References.addReference( 228, "ä"); + html5References.addReference( 228, "ä"); + html5References.addReference( 229, "å"); + html5References.addReference( 229, "å"); + html5References.addReference( 230, "æ"); + html5References.addReference( 230, "æ"); + html5References.addReference( 231, "ç"); + html5References.addReference( 231, "ç"); + html5References.addReference( 232, "è"); + html5References.addReference( 232, "è"); + html5References.addReference( 233, "é"); + html5References.addReference( 233, "é"); + html5References.addReference( 234, "ê"); + html5References.addReference( 234, "ê"); + html5References.addReference( 235, "ë"); + html5References.addReference( 235, "ë"); + html5References.addReference( 236, "ì"); + html5References.addReference( 236, "ì"); + html5References.addReference( 237, "í"); + html5References.addReference( 237, "í"); + html5References.addReference( 238, "î"); + html5References.addReference( 238, "î"); + html5References.addReference( 239, "ï"); + html5References.addReference( 239, "ï"); + html5References.addReference( 240, "ð"); + html5References.addReference( 240, "ð"); + html5References.addReference( 241, "ñ"); + html5References.addReference( 241, "ñ"); + html5References.addReference( 242, "ò"); + html5References.addReference( 242, "ò"); + html5References.addReference( 243, "ó"); + html5References.addReference( 243, "ó"); + html5References.addReference( 244, "ô"); + html5References.addReference( 244, "ô"); + html5References.addReference( 245, "õ"); + html5References.addReference( 245, "õ"); + html5References.addReference( 246, "ö"); + html5References.addReference( 246, "ö"); + html5References.addReference( 247, "÷"); + html5References.addReference( 247, "÷"); + html5References.addReference( 247, "÷"); + html5References.addReference( 248, "ø"); + html5References.addReference( 248, "ø"); + html5References.addReference( 249, "ù"); + html5References.addReference( 249, "ù"); + html5References.addReference( 250, "ú"); + html5References.addReference( 250, "ú"); + html5References.addReference( 251, "û"); + html5References.addReference( 251, "û"); + html5References.addReference( 252, "ü"); + html5References.addReference( 252, "ü"); + html5References.addReference( 253, "ý"); + html5References.addReference( 253, "ý"); + html5References.addReference( 254, "þ"); + html5References.addReference( 254, "þ"); + html5References.addReference( 255, "ÿ"); + html5References.addReference( 255, "ÿ"); + html5References.addReference( 256, "Ā"); + html5References.addReference( 257, "ā"); + html5References.addReference( 258, "Ă"); + html5References.addReference( 259, "ă"); + html5References.addReference( 260, "Ą"); + html5References.addReference( 261, "ą"); + html5References.addReference( 262, "Ć"); + html5References.addReference( 263, "ć"); + html5References.addReference( 264, "Ĉ"); + html5References.addReference( 265, "ĉ"); + html5References.addReference( 266, "Ċ"); + html5References.addReference( 267, "ċ"); + html5References.addReference( 268, "Č"); + html5References.addReference( 269, "č"); + html5References.addReference( 270, "Ď"); + html5References.addReference( 271, "ď"); + html5References.addReference( 272, "Đ"); + html5References.addReference( 273, "đ"); + html5References.addReference( 274, "Ē"); + html5References.addReference( 275, "ē"); + html5References.addReference( 278, "Ė"); + html5References.addReference( 279, "ė"); + html5References.addReference( 280, "Ę"); + html5References.addReference( 281, "ę"); + html5References.addReference( 282, "Ě"); + html5References.addReference( 283, "ě"); + html5References.addReference( 284, "Ĝ"); + html5References.addReference( 285, "ĝ"); + html5References.addReference( 286, "Ğ"); + html5References.addReference( 287, "ğ"); + html5References.addReference( 288, "Ġ"); + html5References.addReference( 289, "ġ"); + html5References.addReference( 290, "Ģ"); + html5References.addReference( 292, "Ĥ"); + html5References.addReference( 293, "ĥ"); + html5References.addReference( 294, "Ħ"); + html5References.addReference( 295, "ħ"); + html5References.addReference( 296, "Ĩ"); + html5References.addReference( 297, "ĩ"); + html5References.addReference( 298, "Ī"); + html5References.addReference( 299, "ī"); + html5References.addReference( 302, "Į"); + html5References.addReference( 303, "į"); + html5References.addReference( 304, "İ"); + html5References.addReference( 305, "ı"); + html5References.addReference( 305, "ı"); + html5References.addReference( 306, "IJ"); + html5References.addReference( 307, "ij"); + html5References.addReference( 308, "Ĵ"); + html5References.addReference( 309, "ĵ"); + html5References.addReference( 310, "Ķ"); + html5References.addReference( 311, "ķ"); + html5References.addReference( 312, "ĸ"); + html5References.addReference( 313, "Ĺ"); + html5References.addReference( 314, "ĺ"); + html5References.addReference( 315, "Ļ"); + html5References.addReference( 316, "ļ"); + html5References.addReference( 317, "Ľ"); + html5References.addReference( 318, "ľ"); + html5References.addReference( 319, "Ŀ"); + html5References.addReference( 320, "ŀ"); + html5References.addReference( 321, "Ł"); + html5References.addReference( 322, "ł"); + html5References.addReference( 323, "Ń"); + html5References.addReference( 324, "ń"); + html5References.addReference( 325, "Ņ"); + html5References.addReference( 326, "ņ"); + html5References.addReference( 327, "Ň"); + html5References.addReference( 328, "ň"); + html5References.addReference( 329, "ʼn"); + html5References.addReference( 330, "Ŋ"); + html5References.addReference( 331, "ŋ"); + html5References.addReference( 332, "Ō"); + html5References.addReference( 333, "ō"); + html5References.addReference( 336, "Ő"); + html5References.addReference( 337, "ő"); + html5References.addReference( 338, "Œ"); + html5References.addReference( 339, "œ"); + html5References.addReference( 340, "Ŕ"); + html5References.addReference( 341, "ŕ"); + html5References.addReference( 342, "Ŗ"); + html5References.addReference( 343, "ŗ"); + html5References.addReference( 344, "Ř"); + html5References.addReference( 345, "ř"); + html5References.addReference( 346, "Ś"); + html5References.addReference( 347, "ś"); + html5References.addReference( 348, "Ŝ"); + html5References.addReference( 349, "ŝ"); + html5References.addReference( 350, "Ş"); + html5References.addReference( 351, "ş"); + html5References.addReference( 352, "Š"); + html5References.addReference( 353, "š"); + html5References.addReference( 354, "Ţ"); + html5References.addReference( 355, "ţ"); + html5References.addReference( 356, "Ť"); + html5References.addReference( 357, "ť"); + html5References.addReference( 358, "Ŧ"); + html5References.addReference( 359, "ŧ"); + html5References.addReference( 360, "Ũ"); + html5References.addReference( 361, "ũ"); + html5References.addReference( 362, "Ū"); + html5References.addReference( 363, "ū"); + html5References.addReference( 364, "Ŭ"); + html5References.addReference( 365, "ŭ"); + html5References.addReference( 366, "Ů"); + html5References.addReference( 367, "ů"); + html5References.addReference( 368, "Ű"); + html5References.addReference( 369, "ű"); + html5References.addReference( 370, "Ų"); + html5References.addReference( 371, "ų"); + html5References.addReference( 372, "Ŵ"); + html5References.addReference( 373, "ŵ"); + html5References.addReference( 374, "Ŷ"); + html5References.addReference( 375, "ŷ"); + html5References.addReference( 376, "Ÿ"); + html5References.addReference( 377, "Ź"); + html5References.addReference( 378, "ź"); + html5References.addReference( 379, "Ż"); + html5References.addReference( 380, "ż"); + html5References.addReference( 381, "Ž"); + html5References.addReference( 382, "ž"); + html5References.addReference( 402, "ƒ"); + html5References.addReference( 437, "Ƶ"); + html5References.addReference( 501, "ǵ"); + html5References.addReference( 567, "ȷ"); + html5References.addReference( 710, "ˆ"); + html5References.addReference( 711, "ˇ"); + html5References.addReference( 711, "ˇ"); + html5References.addReference( 728, "˘"); + html5References.addReference( 728, "˘"); + html5References.addReference( 729, "˙"); + html5References.addReference( 729, "˙"); + html5References.addReference( 730, "˚"); + html5References.addReference( 731, "˛"); + html5References.addReference( 732, "˜"); + html5References.addReference( 732, "˜"); + html5References.addReference( 733, "˝"); + html5References.addReference( 733, "˝"); + html5References.addReference( 785, "̑"); + html5References.addReference( 913, "Α"); + html5References.addReference( 914, "Β"); + html5References.addReference( 915, "Γ"); + html5References.addReference( 916, "Δ"); + html5References.addReference( 917, "Ε"); + html5References.addReference( 918, "Ζ"); + html5References.addReference( 919, "Η"); + html5References.addReference( 920, "Θ"); + html5References.addReference( 921, "Ι"); + html5References.addReference( 922, "Κ"); + html5References.addReference( 923, "Λ"); + html5References.addReference( 924, "Μ"); + html5References.addReference( 925, "Ν"); + html5References.addReference( 926, "Ξ"); + html5References.addReference( 927, "Ο"); + html5References.addReference( 928, "Π"); + html5References.addReference( 929, "Ρ"); + html5References.addReference( 931, "Σ"); + html5References.addReference( 932, "Τ"); + html5References.addReference( 933, "Υ"); + html5References.addReference( 934, "Φ"); + html5References.addReference( 935, "Χ"); + html5References.addReference( 936, "Ψ"); + html5References.addReference( 937, "Ω"); + html5References.addReference( 937, "Ω"); + html5References.addReference( 945, "α"); + html5References.addReference( 946, "β"); + html5References.addReference( 947, "γ"); + html5References.addReference( 948, "δ"); + html5References.addReference( 949, "ε"); + html5References.addReference( 949, "ε"); + html5References.addReference( 950, "ζ"); + html5References.addReference( 951, "η"); + html5References.addReference( 952, "θ"); + html5References.addReference( 953, "ι"); + html5References.addReference( 954, "κ"); + html5References.addReference( 955, "λ"); + html5References.addReference( 956, "μ"); + html5References.addReference( 957, "ν"); + html5References.addReference( 958, "ξ"); + html5References.addReference( 959, "ο"); + html5References.addReference( 960, "π"); + html5References.addReference( 961, "ρ"); + html5References.addReference( 962, "ς"); + html5References.addReference( 962, "ς"); + html5References.addReference( 962, "ς"); + html5References.addReference( 963, "σ"); + html5References.addReference( 964, "τ"); + html5References.addReference( 965, "υ"); + html5References.addReference( 965, "υ"); + html5References.addReference( 966, "φ"); + html5References.addReference( 967, "χ"); + html5References.addReference( 968, "ψ"); + html5References.addReference( 969, "ω"); + html5References.addReference( 977, "ϑ"); + html5References.addReference( 977, "ϑ"); + html5References.addReference( 977, "ϑ"); + html5References.addReference( 978, "ϒ"); + html5References.addReference( 978, "ϒ"); + html5References.addReference( 981, "ϕ"); + html5References.addReference( 981, "ϕ"); + html5References.addReference( 981, "ϕ"); + html5References.addReference( 982, "ϖ"); + html5References.addReference( 982, "ϖ"); + html5References.addReference( 988, "Ϝ"); + html5References.addReference( 989, "ϝ"); + html5References.addReference( 989, "ϝ"); + html5References.addReference( 1008, "ϰ"); + html5References.addReference( 1008, "ϰ"); + html5References.addReference( 1009, "ϱ"); + html5References.addReference( 1009, "ϱ"); + html5References.addReference( 1013, "ϵ"); + html5References.addReference( 1013, "ϵ"); + html5References.addReference( 1013, "ϵ"); + html5References.addReference( 1014, "϶"); + html5References.addReference( 1014, "϶"); + html5References.addReference( 1025, "Ё"); + html5References.addReference( 1026, "Ђ"); + html5References.addReference( 1027, "Ѓ"); + html5References.addReference( 1028, "Є"); + html5References.addReference( 1029, "Ѕ"); + html5References.addReference( 1030, "І"); + html5References.addReference( 1031, "Ї"); + html5References.addReference( 1032, "Ј"); + html5References.addReference( 1033, "Љ"); + html5References.addReference( 1034, "Њ"); + html5References.addReference( 1035, "Ћ"); + html5References.addReference( 1036, "Ќ"); + html5References.addReference( 1038, "Ў"); + html5References.addReference( 1039, "Џ"); + html5References.addReference( 1040, "А"); + html5References.addReference( 1041, "Б"); + html5References.addReference( 1042, "В"); + html5References.addReference( 1043, "Г"); + html5References.addReference( 1044, "Д"); + html5References.addReference( 1045, "Е"); + html5References.addReference( 1046, "Ж"); + html5References.addReference( 1047, "З"); + html5References.addReference( 1048, "И"); + html5References.addReference( 1049, "Й"); + html5References.addReference( 1050, "К"); + html5References.addReference( 1051, "Л"); + html5References.addReference( 1052, "М"); + html5References.addReference( 1053, "Н"); + html5References.addReference( 1054, "О"); + html5References.addReference( 1055, "П"); + html5References.addReference( 1056, "Р"); + html5References.addReference( 1057, "С"); + html5References.addReference( 1058, "Т"); + html5References.addReference( 1059, "У"); + html5References.addReference( 1060, "Ф"); + html5References.addReference( 1061, "Х"); + html5References.addReference( 1062, "Ц"); + html5References.addReference( 1063, "Ч"); + html5References.addReference( 1064, "Ш"); + html5References.addReference( 1065, "Щ"); + html5References.addReference( 1066, "Ъ"); + html5References.addReference( 1067, "Ы"); + html5References.addReference( 1068, "Ь"); + html5References.addReference( 1069, "Э"); + html5References.addReference( 1070, "Ю"); + html5References.addReference( 1071, "Я"); + html5References.addReference( 1072, "а"); + html5References.addReference( 1073, "б"); + html5References.addReference( 1074, "в"); + html5References.addReference( 1075, "г"); + html5References.addReference( 1076, "д"); + html5References.addReference( 1077, "е"); + html5References.addReference( 1078, "ж"); + html5References.addReference( 1079, "з"); + html5References.addReference( 1080, "и"); + html5References.addReference( 1081, "й"); + html5References.addReference( 1082, "к"); + html5References.addReference( 1083, "л"); + html5References.addReference( 1084, "м"); + html5References.addReference( 1085, "н"); + html5References.addReference( 1086, "о"); + html5References.addReference( 1087, "п"); + html5References.addReference( 1088, "р"); + html5References.addReference( 1089, "с"); + html5References.addReference( 1090, "т"); + html5References.addReference( 1091, "у"); + html5References.addReference( 1092, "ф"); + html5References.addReference( 1093, "х"); + html5References.addReference( 1094, "ц"); + html5References.addReference( 1095, "ч"); + html5References.addReference( 1096, "ш"); + html5References.addReference( 1097, "щ"); + html5References.addReference( 1098, "ъ"); + html5References.addReference( 1099, "ы"); + html5References.addReference( 1100, "ь"); + html5References.addReference( 1101, "э"); + html5References.addReference( 1102, "ю"); + html5References.addReference( 1103, "я"); + html5References.addReference( 1105, "ё"); + html5References.addReference( 1106, "ђ"); + html5References.addReference( 1107, "ѓ"); + html5References.addReference( 1108, "є"); + html5References.addReference( 1109, "ѕ"); + html5References.addReference( 1110, "і"); + html5References.addReference( 1111, "ї"); + html5References.addReference( 1112, "ј"); + html5References.addReference( 1113, "љ"); + html5References.addReference( 1114, "њ"); + html5References.addReference( 1115, "ћ"); + html5References.addReference( 1116, "ќ"); + html5References.addReference( 1118, "ў"); + html5References.addReference( 1119, "џ"); + html5References.addReference( 8194, " "); + html5References.addReference( 8195, " "); + html5References.addReference( 8196, " "); + html5References.addReference( 8197, " "); + html5References.addReference( 8199, " "); + html5References.addReference( 8200, " "); + html5References.addReference( 8201, " "); + html5References.addReference( 8201, " "); + html5References.addReference( 8202, " "); + html5References.addReference( 8202, " "); + html5References.addReference( 8203, "​"); + html5References.addReference( 8203, "​"); + html5References.addReference( 8203, "​"); + html5References.addReference( 8203, "​"); + html5References.addReference( 8203, "​"); + html5References.addReference( 8204, "‌"); + html5References.addReference( 8205, "‍"); + html5References.addReference( 8206, "‎"); + html5References.addReference( 8207, "‏"); + html5References.addReference( 8208, "‐"); + html5References.addReference( 8208, "‐"); + html5References.addReference( 8211, "–"); + html5References.addReference( 8212, "—"); + html5References.addReference( 8213, "―"); + html5References.addReference( 8214, "‖"); + html5References.addReference( 8214, "‖"); + html5References.addReference( 8216, "‘"); + html5References.addReference( 8216, "‘"); + html5References.addReference( 8217, "’"); + html5References.addReference( 8217, "’"); + html5References.addReference( 8217, "’"); + html5References.addReference( 8218, "‚"); + html5References.addReference( 8218, "‚"); + html5References.addReference( 8220, "“"); + html5References.addReference( 8220, "“"); + html5References.addReference( 8221, "”"); + html5References.addReference( 8221, "”"); + html5References.addReference( 8221, "”"); + html5References.addReference( 8222, "„"); + html5References.addReference( 8222, "„"); + html5References.addReference( 8224, "†"); + html5References.addReference( 8225, "‡"); + html5References.addReference( 8225, "‡"); + html5References.addReference( 8226, "•"); + html5References.addReference( 8226, "•"); + html5References.addReference( 8229, "‥"); + html5References.addReference( 8230, "…"); + html5References.addReference( 8230, "…"); + html5References.addReference( 8240, "‰"); + html5References.addReference( 8241, "‱"); + html5References.addReference( 8242, "′"); + html5References.addReference( 8243, "″"); + html5References.addReference( 8244, "‴"); + html5References.addReference( 8245, "‵"); + html5References.addReference( 8245, "‵"); + html5References.addReference( 8249, "‹"); + html5References.addReference( 8250, "›"); + html5References.addReference( 8254, "‾"); + html5References.addReference( 8254, "‾"); + html5References.addReference( 8257, "⁁"); + html5References.addReference( 8259, "⁃"); + html5References.addReference( 8260, "⁄"); + html5References.addReference( 8271, "⁏"); + html5References.addReference( 8279, "⁗"); + html5References.addReference( 8287, " "); + html5References.addReference( 8287, 8202, "  "); + html5References.addReference( 8288, "⁠"); + html5References.addReference( 8289, "⁡"); + html5References.addReference( 8289, "⁡"); + html5References.addReference( 8290, "⁢"); + html5References.addReference( 8290, "⁢"); + html5References.addReference( 8291, "⁣"); + html5References.addReference( 8291, "⁣"); + html5References.addReference( 8364, "€"); + html5References.addReference( 8411, "⃛"); + html5References.addReference( 8411, "⃛"); + html5References.addReference( 8412, "⃜"); + html5References.addReference( 8450, "ℂ"); + html5References.addReference( 8450, "ℂ"); + html5References.addReference( 8453, "℅"); + html5References.addReference( 8458, "ℊ"); + html5References.addReference( 8459, "ℋ"); + html5References.addReference( 8459, "ℋ"); + html5References.addReference( 8459, "ℋ"); + html5References.addReference( 8460, "ℌ"); + html5References.addReference( 8460, "ℌ"); + html5References.addReference( 8461, "ℍ"); + html5References.addReference( 8461, "ℍ"); + html5References.addReference( 8462, "ℎ"); + html5References.addReference( 8463, "ℏ"); + html5References.addReference( 8463, "ℏ"); + html5References.addReference( 8463, "ℏ"); + html5References.addReference( 8463, "ℏ"); + html5References.addReference( 8464, "ℐ"); + html5References.addReference( 8464, "ℐ"); + html5References.addReference( 8465, "ℑ"); + html5References.addReference( 8465, "ℑ"); + html5References.addReference( 8465, "ℑ"); + html5References.addReference( 8465, "ℑ"); + html5References.addReference( 8466, "ℒ"); + html5References.addReference( 8466, "ℒ"); + html5References.addReference( 8466, "ℒ"); + html5References.addReference( 8467, "ℓ"); + html5References.addReference( 8469, "ℕ"); + html5References.addReference( 8469, "ℕ"); + html5References.addReference( 8470, "№"); + html5References.addReference( 8471, "℗"); + html5References.addReference( 8472, "℘"); + html5References.addReference( 8472, "℘"); + html5References.addReference( 8473, "ℙ"); + html5References.addReference( 8473, "ℙ"); + html5References.addReference( 8474, "ℚ"); + html5References.addReference( 8474, "ℚ"); + html5References.addReference( 8475, "ℛ"); + html5References.addReference( 8475, "ℛ"); + html5References.addReference( 8476, "ℜ"); + html5References.addReference( 8476, "ℜ"); + html5References.addReference( 8476, "ℜ"); + html5References.addReference( 8476, "ℜ"); + html5References.addReference( 8477, "ℝ"); + html5References.addReference( 8477, "ℝ"); + html5References.addReference( 8478, "℞"); + html5References.addReference( 8482, "™"); + html5References.addReference( 8482, "™"); + html5References.addReference( 8484, "ℤ"); + html5References.addReference( 8484, "ℤ"); + html5References.addReference( 8487, "℧"); + html5References.addReference( 8488, "ℨ"); + html5References.addReference( 8488, "ℨ"); + html5References.addReference( 8489, "℩"); + html5References.addReference( 8492, "ℬ"); + html5References.addReference( 8492, "ℬ"); + html5References.addReference( 8492, "ℬ"); + html5References.addReference( 8493, "ℭ"); + html5References.addReference( 8493, "ℭ"); + html5References.addReference( 8495, "ℯ"); + html5References.addReference( 8496, "ℰ"); + html5References.addReference( 8496, "ℰ"); + html5References.addReference( 8497, "ℱ"); + html5References.addReference( 8497, "ℱ"); + html5References.addReference( 8499, "ℳ"); + html5References.addReference( 8499, "ℳ"); + html5References.addReference( 8499, "ℳ"); + html5References.addReference( 8500, "ℴ"); + html5References.addReference( 8500, "ℴ"); + html5References.addReference( 8500, "ℴ"); + html5References.addReference( 8501, "ℵ"); + html5References.addReference( 8501, "ℵ"); + html5References.addReference( 8502, "ℶ"); + html5References.addReference( 8503, "ℷ"); + html5References.addReference( 8504, "ℸ"); + html5References.addReference( 8517, "ⅅ"); + html5References.addReference( 8517, "ⅅ"); + html5References.addReference( 8518, "ⅆ"); + html5References.addReference( 8518, "ⅆ"); + html5References.addReference( 8519, "ⅇ"); + html5References.addReference( 8519, "ⅇ"); + html5References.addReference( 8519, "ⅇ"); + html5References.addReference( 8520, "ⅈ"); + html5References.addReference( 8520, "ⅈ"); + html5References.addReference( 8531, "⅓"); + html5References.addReference( 8532, "⅔"); + html5References.addReference( 8533, "⅕"); + html5References.addReference( 8534, "⅖"); + html5References.addReference( 8535, "⅗"); + html5References.addReference( 8536, "⅘"); + html5References.addReference( 8537, "⅙"); + html5References.addReference( 8538, "⅚"); + html5References.addReference( 8539, "⅛"); + html5References.addReference( 8540, "⅜"); + html5References.addReference( 8541, "⅝"); + html5References.addReference( 8542, "⅞"); + html5References.addReference( 8592, "←"); + html5References.addReference( 8592, "←"); + html5References.addReference( 8592, "←"); + html5References.addReference( 8592, "←"); + html5References.addReference( 8592, "←"); + html5References.addReference( 8593, "↑"); + html5References.addReference( 8593, "↑"); + html5References.addReference( 8593, "↑"); + html5References.addReference( 8593, "↑"); + html5References.addReference( 8594, "→"); + html5References.addReference( 8594, "→"); + html5References.addReference( 8594, "→"); + html5References.addReference( 8594, "→"); + html5References.addReference( 8594, "→"); + html5References.addReference( 8595, "↓"); + html5References.addReference( 8595, "↓"); + html5References.addReference( 8595, "↓"); + html5References.addReference( 8595, "↓"); + html5References.addReference( 8596, "↔"); + html5References.addReference( 8596, "↔"); + html5References.addReference( 8596, "↔"); + html5References.addReference( 8597, "↕"); + html5References.addReference( 8597, "↕"); + html5References.addReference( 8597, "↕"); + html5References.addReference( 8598, "↖"); + html5References.addReference( 8598, "↖"); + html5References.addReference( 8598, "↖"); + html5References.addReference( 8599, "↗"); + html5References.addReference( 8599, "↗"); + html5References.addReference( 8599, "↗"); + html5References.addReference( 8600, "↘"); + html5References.addReference( 8600, "↘"); + html5References.addReference( 8600, "↘"); + html5References.addReference( 8601, "↙"); + html5References.addReference( 8601, "↙"); + html5References.addReference( 8601, "↙"); + html5References.addReference( 8602, "↚"); + html5References.addReference( 8602, "↚"); + html5References.addReference( 8603, "↛"); + html5References.addReference( 8603, "↛"); + html5References.addReference( 8605, "↝"); + html5References.addReference( 8605, "↝"); + html5References.addReference( 8605, 824, "↝̸"); + html5References.addReference( 8606, "↞"); + html5References.addReference( 8606, "↞"); + html5References.addReference( 8607, "↟"); + html5References.addReference( 8608, "↠"); + html5References.addReference( 8608, "↠"); + html5References.addReference( 8609, "↡"); + html5References.addReference( 8610, "↢"); + html5References.addReference( 8610, "↢"); + html5References.addReference( 8611, "↣"); + html5References.addReference( 8611, "↣"); + html5References.addReference( 8612, "↤"); + html5References.addReference( 8612, "↤"); + html5References.addReference( 8613, "↥"); + html5References.addReference( 8613, "↥"); + html5References.addReference( 8614, "↦"); + html5References.addReference( 8614, "↦"); + html5References.addReference( 8614, "↦"); + html5References.addReference( 8615, "↧"); + html5References.addReference( 8615, "↧"); + html5References.addReference( 8617, "↩"); + html5References.addReference( 8617, "↩"); + html5References.addReference( 8618, "↪"); + html5References.addReference( 8618, "↪"); + html5References.addReference( 8619, "↫"); + html5References.addReference( 8619, "↫"); + html5References.addReference( 8620, "↬"); + html5References.addReference( 8620, "↬"); + html5References.addReference( 8621, "↭"); + html5References.addReference( 8621, "↭"); + html5References.addReference( 8622, "↮"); + html5References.addReference( 8622, "↮"); + html5References.addReference( 8624, "↰"); + html5References.addReference( 8624, "↰"); + html5References.addReference( 8625, "↱"); + html5References.addReference( 8625, "↱"); + html5References.addReference( 8626, "↲"); + html5References.addReference( 8627, "↳"); + html5References.addReference( 8629, "↵"); + html5References.addReference( 8630, "↶"); + html5References.addReference( 8630, "↶"); + html5References.addReference( 8631, "↷"); + html5References.addReference( 8631, "↷"); + html5References.addReference( 8634, "↺"); + html5References.addReference( 8634, "↺"); + html5References.addReference( 8635, "↻"); + html5References.addReference( 8635, "↻"); + html5References.addReference( 8636, "↼"); + html5References.addReference( 8636, "↼"); + html5References.addReference( 8636, "↼"); + html5References.addReference( 8637, "↽"); + html5References.addReference( 8637, "↽"); + html5References.addReference( 8637, "↽"); + html5References.addReference( 8638, "↾"); + html5References.addReference( 8638, "↾"); + html5References.addReference( 8638, "↾"); + html5References.addReference( 8639, "↿"); + html5References.addReference( 8639, "↿"); + html5References.addReference( 8639, "↿"); + html5References.addReference( 8640, "⇀"); + html5References.addReference( 8640, "⇀"); + html5References.addReference( 8640, "⇀"); + html5References.addReference( 8641, "⇁"); + html5References.addReference( 8641, "⇁"); + html5References.addReference( 8641, "⇁"); + html5References.addReference( 8642, "⇂"); + html5References.addReference( 8642, "⇂"); + html5References.addReference( 8642, "⇂"); + html5References.addReference( 8643, "⇃"); + html5References.addReference( 8643, "⇃"); + html5References.addReference( 8643, "⇃"); + html5References.addReference( 8644, "⇄"); + html5References.addReference( 8644, "⇄"); + html5References.addReference( 8644, "⇄"); + html5References.addReference( 8645, "⇅"); + html5References.addReference( 8645, "⇅"); + html5References.addReference( 8646, "⇆"); + html5References.addReference( 8646, "⇆"); + html5References.addReference( 8646, "⇆"); + html5References.addReference( 8647, "⇇"); + html5References.addReference( 8647, "⇇"); + html5References.addReference( 8648, "⇈"); + html5References.addReference( 8648, "⇈"); + html5References.addReference( 8649, "⇉"); + html5References.addReference( 8649, "⇉"); + html5References.addReference( 8650, "⇊"); + html5References.addReference( 8650, "⇊"); + html5References.addReference( 8651, "⇋"); + html5References.addReference( 8651, "⇋"); + html5References.addReference( 8651, "⇋"); + html5References.addReference( 8652, "⇌"); + html5References.addReference( 8652, "⇌"); + html5References.addReference( 8652, "⇌"); + html5References.addReference( 8653, "⇍"); + html5References.addReference( 8653, "⇍"); + html5References.addReference( 8654, "⇎"); + html5References.addReference( 8654, "⇎"); + html5References.addReference( 8655, "⇏"); + html5References.addReference( 8655, "⇏"); + html5References.addReference( 8656, "⇐"); + html5References.addReference( 8656, "⇐"); + html5References.addReference( 8656, "⇐"); + html5References.addReference( 8657, "⇑"); + html5References.addReference( 8657, "⇑"); + html5References.addReference( 8657, "⇑"); + html5References.addReference( 8658, "⇒"); + html5References.addReference( 8658, "⇒"); + html5References.addReference( 8658, "⇒"); + html5References.addReference( 8658, "⇒"); + html5References.addReference( 8659, "⇓"); + html5References.addReference( 8659, "⇓"); + html5References.addReference( 8659, "⇓"); + html5References.addReference( 8660, "⇔"); + html5References.addReference( 8660, "⇔"); + html5References.addReference( 8660, "⇔"); + html5References.addReference( 8660, "⇔"); + html5References.addReference( 8661, "⇕"); + html5References.addReference( 8661, "⇕"); + html5References.addReference( 8661, "⇕"); + html5References.addReference( 8662, "⇖"); + html5References.addReference( 8663, "⇗"); + html5References.addReference( 8664, "⇘"); + html5References.addReference( 8665, "⇙"); + html5References.addReference( 8666, "⇚"); + html5References.addReference( 8666, "⇚"); + html5References.addReference( 8667, "⇛"); + html5References.addReference( 8667, "⇛"); + html5References.addReference( 8669, "⇝"); + html5References.addReference( 8676, "⇤"); + html5References.addReference( 8676, "⇤"); + html5References.addReference( 8677, "⇥"); + html5References.addReference( 8677, "⇥"); + html5References.addReference( 8693, "⇵"); + html5References.addReference( 8693, "⇵"); + html5References.addReference( 8701, "⇽"); + html5References.addReference( 8702, "⇾"); + html5References.addReference( 8703, "⇿"); + html5References.addReference( 8704, "∀"); + html5References.addReference( 8704, "∀"); + html5References.addReference( 8705, "∁"); + html5References.addReference( 8705, "∁"); + html5References.addReference( 8706, "∂"); + html5References.addReference( 8706, "∂"); + html5References.addReference( 8706, 824, "∂̸"); + html5References.addReference( 8707, "∃"); + html5References.addReference( 8707, "∃"); + html5References.addReference( 8708, "∄"); + html5References.addReference( 8708, "∄"); + html5References.addReference( 8708, "∄"); + html5References.addReference( 8709, "∅"); + html5References.addReference( 8709, "∅"); + html5References.addReference( 8709, "∅"); + html5References.addReference( 8709, "∅"); + html5References.addReference( 8711, "∇"); + html5References.addReference( 8711, "∇"); + html5References.addReference( 8712, "∈"); + html5References.addReference( 8712, "∈"); + html5References.addReference( 8712, "∈"); + html5References.addReference( 8712, "∈"); + html5References.addReference( 8713, "∉"); + html5References.addReference( 8713, "∉"); + html5References.addReference( 8713, "∉"); + html5References.addReference( 8715, "∋"); + html5References.addReference( 8715, "∋"); + html5References.addReference( 8715, "∋"); + html5References.addReference( 8715, "∋"); + html5References.addReference( 8716, "∌"); + html5References.addReference( 8716, "∌"); + html5References.addReference( 8716, "∌"); + html5References.addReference( 8719, "∏"); + html5References.addReference( 8719, "∏"); + html5References.addReference( 8720, "∐"); + html5References.addReference( 8720, "∐"); + html5References.addReference( 8721, "∑"); + html5References.addReference( 8721, "∑"); + html5References.addReference( 8722, "−"); + html5References.addReference( 8723, "∓"); + html5References.addReference( 8723, "∓"); + html5References.addReference( 8723, "∓"); + html5References.addReference( 8724, "∔"); + html5References.addReference( 8724, "∔"); + html5References.addReference( 8726, "∖"); + html5References.addReference( 8726, "∖"); + html5References.addReference( 8726, "∖"); + html5References.addReference( 8726, "∖"); + html5References.addReference( 8726, "∖"); + html5References.addReference( 8727, "∗"); + html5References.addReference( 8728, "∘"); + html5References.addReference( 8728, "∘"); + html5References.addReference( 8730, "√"); + html5References.addReference( 8730, "√"); + html5References.addReference( 8733, "∝"); + html5References.addReference( 8733, "∝"); + html5References.addReference( 8733, "∝"); + html5References.addReference( 8733, "∝"); + html5References.addReference( 8733, "∝"); + html5References.addReference( 8734, "∞"); + html5References.addReference( 8735, "∟"); + html5References.addReference( 8736, "∠"); + html5References.addReference( 8736, "∠"); + html5References.addReference( 8736, 8402, "∠⃒"); + html5References.addReference( 8737, "∡"); + html5References.addReference( 8737, "∡"); + html5References.addReference( 8738, "∢"); + html5References.addReference( 8739, "∣"); + html5References.addReference( 8739, "∣"); + html5References.addReference( 8739, "∣"); + html5References.addReference( 8739, "∣"); + html5References.addReference( 8740, "∤"); + html5References.addReference( 8740, "∤"); + html5References.addReference( 8740, "∤"); + html5References.addReference( 8740, "∤"); + html5References.addReference( 8741, "∥"); + html5References.addReference( 8741, "∥"); + html5References.addReference( 8741, "∥"); + html5References.addReference( 8741, "∥"); + html5References.addReference( 8741, "∥"); + html5References.addReference( 8742, "∦"); + html5References.addReference( 8742, "∦"); + html5References.addReference( 8742, "∦"); + html5References.addReference( 8742, "∦"); + html5References.addReference( 8742, "∦"); + html5References.addReference( 8743, "∧"); + html5References.addReference( 8743, "∧"); + html5References.addReference( 8744, "∨"); + html5References.addReference( 8744, "∨"); + html5References.addReference( 8745, "∩"); + html5References.addReference( 8745, 65024, "∩︀"); + html5References.addReference( 8746, "∪"); + html5References.addReference( 8746, 65024, "∪︀"); + html5References.addReference( 8747, "∫"); + html5References.addReference( 8747, "∫"); + html5References.addReference( 8748, "∬"); + html5References.addReference( 8749, "∭"); + html5References.addReference( 8749, "∭"); + html5References.addReference( 8750, "∮"); + html5References.addReference( 8750, "∮"); + html5References.addReference( 8750, "∮"); + html5References.addReference( 8751, "∯"); + html5References.addReference( 8751, "∯"); + html5References.addReference( 8752, "∰"); + html5References.addReference( 8753, "∱"); + html5References.addReference( 8754, "∲"); + html5References.addReference( 8754, "∲"); + html5References.addReference( 8755, "∳"); + html5References.addReference( 8755, "∳"); + html5References.addReference( 8756, "∴"); + html5References.addReference( 8756, "∴"); + html5References.addReference( 8756, "∴"); + html5References.addReference( 8757, "∵"); + html5References.addReference( 8757, "∵"); + html5References.addReference( 8757, "∵"); + html5References.addReference( 8758, "∶"); + html5References.addReference( 8759, "∷"); + html5References.addReference( 8759, "∷"); + html5References.addReference( 8760, "∸"); + html5References.addReference( 8760, "∸"); + html5References.addReference( 8762, "∺"); + html5References.addReference( 8763, "∻"); + html5References.addReference( 8764, "∼"); + html5References.addReference( 8764, "∼"); + html5References.addReference( 8764, "∼"); + html5References.addReference( 8764, "∼"); + html5References.addReference( 8764, 8402, "∼⃒"); + html5References.addReference( 8765, "∽"); + html5References.addReference( 8765, "∽"); + html5References.addReference( 8765, 817, "∽̱"); + html5References.addReference( 8766, "∾"); + html5References.addReference( 8766, "∾"); + html5References.addReference( 8766, 819, "∾̳"); + html5References.addReference( 8767, "∿"); + html5References.addReference( 8768, "≀"); + html5References.addReference( 8768, "≀"); + html5References.addReference( 8768, "≀"); + html5References.addReference( 8769, "≁"); + html5References.addReference( 8769, "≁"); + html5References.addReference( 8770, "≂"); + html5References.addReference( 8770, "≂"); + html5References.addReference( 8770, "≂"); + html5References.addReference( 8770, 824, "≂̸"); + html5References.addReference( 8770, 824, "≂̸"); + html5References.addReference( 8771, "≃"); + html5References.addReference( 8771, "≃"); + html5References.addReference( 8771, "≃"); + html5References.addReference( 8772, "≄"); + html5References.addReference( 8772, "≄"); + html5References.addReference( 8772, "≄"); + html5References.addReference( 8773, "≅"); + html5References.addReference( 8773, "≅"); + html5References.addReference( 8774, "≆"); + html5References.addReference( 8775, "≇"); + html5References.addReference( 8775, "≇"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8776, "≈"); + html5References.addReference( 8777, "≉"); + html5References.addReference( 8777, "≉"); + html5References.addReference( 8777, "≉"); + html5References.addReference( 8778, "≊"); + html5References.addReference( 8778, "≊"); + html5References.addReference( 8779, "≋"); + html5References.addReference( 8779, 824, "≋̸"); + html5References.addReference( 8780, "≌"); + html5References.addReference( 8780, "≌"); + html5References.addReference( 8781, "≍"); + html5References.addReference( 8781, "≍"); + html5References.addReference( 8781, 8402, "≍⃒"); + html5References.addReference( 8782, "≎"); + html5References.addReference( 8782, "≎"); + html5References.addReference( 8782, "≎"); + html5References.addReference( 8782, 824, "≎̸"); + html5References.addReference( 8782, 824, "≎̸"); + html5References.addReference( 8783, "≏"); + html5References.addReference( 8783, "≏"); + html5References.addReference( 8783, "≏"); + html5References.addReference( 8783, 824, "≏̸"); + html5References.addReference( 8783, 824, "≏̸"); + html5References.addReference( 8784, "≐"); + html5References.addReference( 8784, "≐"); + html5References.addReference( 8784, "≐"); + html5References.addReference( 8784, 824, "≐̸"); + html5References.addReference( 8785, "≑"); + html5References.addReference( 8785, "≑"); + html5References.addReference( 8786, "≒"); + html5References.addReference( 8786, "≒"); + html5References.addReference( 8787, "≓"); + html5References.addReference( 8787, "≓"); + html5References.addReference( 8788, "≔"); + html5References.addReference( 8788, "≔"); + html5References.addReference( 8788, "≔"); + html5References.addReference( 8789, "≕"); + html5References.addReference( 8789, "≕"); + html5References.addReference( 8790, "≖"); + html5References.addReference( 8790, "≖"); + html5References.addReference( 8791, "≗"); + html5References.addReference( 8791, "≗"); + html5References.addReference( 8793, "≙"); + html5References.addReference( 8794, "≚"); + html5References.addReference( 8796, "≜"); + html5References.addReference( 8796, "≜"); + html5References.addReference( 8799, "≟"); + html5References.addReference( 8799, "≟"); + html5References.addReference( 8800, "≠"); + html5References.addReference( 8800, "≠"); + html5References.addReference( 8801, "≡"); + html5References.addReference( 8801, "≡"); + html5References.addReference( 8801, 8421, "≡⃥"); + html5References.addReference( 8802, "≢"); + html5References.addReference( 8802, "≢"); + html5References.addReference( 8804, "≤"); + html5References.addReference( 8804, "≤"); + html5References.addReference( 8804, 8402, "≤⃒"); + html5References.addReference( 8805, "≥"); + html5References.addReference( 8805, "≥"); + html5References.addReference( 8805, "≥"); + html5References.addReference( 8805, 8402, "≥⃒"); + html5References.addReference( 8806, "≦"); + html5References.addReference( 8806, "≦"); + html5References.addReference( 8806, "≦"); + html5References.addReference( 8806, 824, "≦̸"); + html5References.addReference( 8806, 824, "≦̸"); + html5References.addReference( 8807, "≧"); + html5References.addReference( 8807, "≧"); + html5References.addReference( 8807, "≧"); + html5References.addReference( 8807, 824, "≧̸"); + html5References.addReference( 8807, 824, "≧̸"); + html5References.addReference( 8807, 824, "≧̸"); + html5References.addReference( 8808, "≨"); + html5References.addReference( 8808, "≨"); + html5References.addReference( 8808, 65024, "≨︀"); + html5References.addReference( 8808, 65024, "≨︀"); + html5References.addReference( 8809, "≩"); + html5References.addReference( 8809, "≩"); + html5References.addReference( 8809, 65024, "≩︀"); + html5References.addReference( 8809, 65024, "≩︀"); + html5References.addReference( 8810, "≪"); + html5References.addReference( 8810, "≪"); + html5References.addReference( 8810, "≪"); + html5References.addReference( 8810, 824, "≪̸"); + html5References.addReference( 8810, 824, "≪̸"); + html5References.addReference( 8810, 8402, "≪⃒"); + html5References.addReference( 8811, "≫"); + html5References.addReference( 8811, "≫"); + html5References.addReference( 8811, "≫"); + html5References.addReference( 8811, 824, "≫̸"); + html5References.addReference( 8811, 824, "≫̸"); + html5References.addReference( 8811, 8402, "≫⃒"); + html5References.addReference( 8812, "≬"); + html5References.addReference( 8812, "≬"); + html5References.addReference( 8813, "≭"); + html5References.addReference( 8814, "≮"); + html5References.addReference( 8814, "≮"); + html5References.addReference( 8814, "≮"); + html5References.addReference( 8815, "≯"); + html5References.addReference( 8815, "≯"); + html5References.addReference( 8815, "≯"); + html5References.addReference( 8816, "≰"); + html5References.addReference( 8816, "≰"); + html5References.addReference( 8816, "≰"); + html5References.addReference( 8817, "≱"); + html5References.addReference( 8817, "≱"); + html5References.addReference( 8817, "≱"); + html5References.addReference( 8818, "≲"); + html5References.addReference( 8818, "≲"); + html5References.addReference( 8818, "≲"); + html5References.addReference( 8819, "≳"); + html5References.addReference( 8819, "≳"); + html5References.addReference( 8819, "≳"); + html5References.addReference( 8820, "≴"); + html5References.addReference( 8820, "≴"); + html5References.addReference( 8821, "≵"); + html5References.addReference( 8821, "≵"); + html5References.addReference( 8822, "≶"); + html5References.addReference( 8822, "≶"); + html5References.addReference( 8822, "≶"); + html5References.addReference( 8823, "≷"); + html5References.addReference( 8823, "≷"); + html5References.addReference( 8823, "≷"); + html5References.addReference( 8824, "≸"); + html5References.addReference( 8824, "≸"); + html5References.addReference( 8825, "≹"); + html5References.addReference( 8825, "≹"); + html5References.addReference( 8826, "≺"); + html5References.addReference( 8826, "≺"); + html5References.addReference( 8826, "≺"); + html5References.addReference( 8827, "≻"); + html5References.addReference( 8827, "≻"); + html5References.addReference( 8827, "≻"); + html5References.addReference( 8828, "≼"); + html5References.addReference( 8828, "≼"); + html5References.addReference( 8828, "≼"); + html5References.addReference( 8829, "≽"); + html5References.addReference( 8829, "≽"); + html5References.addReference( 8829, "≽"); + html5References.addReference( 8830, "≾"); + html5References.addReference( 8830, "≾"); + html5References.addReference( 8830, "≾"); + html5References.addReference( 8831, "≿"); + html5References.addReference( 8831, "≿"); + html5References.addReference( 8831, "≿"); + html5References.addReference( 8831, 824, "≿̸"); + html5References.addReference( 8832, "⊀"); + html5References.addReference( 8832, "⊀"); + html5References.addReference( 8832, "⊀"); + html5References.addReference( 8833, "⊁"); + html5References.addReference( 8833, "⊁"); + html5References.addReference( 8833, "⊁"); + html5References.addReference( 8834, "⊂"); + html5References.addReference( 8834, "⊂"); + html5References.addReference( 8834, 8402, "⊂⃒"); + html5References.addReference( 8834, 8402, "⊂⃒"); + html5References.addReference( 8834, 8402, "⊂⃒"); + html5References.addReference( 8835, "⊃"); + html5References.addReference( 8835, "⊃"); + html5References.addReference( 8835, "⊃"); + html5References.addReference( 8835, 8402, "⊃⃒"); + html5References.addReference( 8835, 8402, "⊃⃒"); + html5References.addReference( 8835, 8402, "⊃⃒"); + html5References.addReference( 8836, "⊄"); + html5References.addReference( 8837, "⊅"); + html5References.addReference( 8838, "⊆"); + html5References.addReference( 8838, "⊆"); + html5References.addReference( 8838, "⊆"); + html5References.addReference( 8839, "⊇"); + html5References.addReference( 8839, "⊇"); + html5References.addReference( 8839, "⊇"); + html5References.addReference( 8840, "⊈"); + html5References.addReference( 8840, "⊈"); + html5References.addReference( 8840, "⊈"); + html5References.addReference( 8841, "⊉"); + html5References.addReference( 8841, "⊉"); + html5References.addReference( 8841, "⊉"); + html5References.addReference( 8842, "⊊"); + html5References.addReference( 8842, "⊊"); + html5References.addReference( 8842, 65024, "⊊︀"); + html5References.addReference( 8842, 65024, "⊊︀"); + html5References.addReference( 8843, "⊋"); + html5References.addReference( 8843, "⊋"); + html5References.addReference( 8843, 65024, "⊋︀"); + html5References.addReference( 8843, 65024, "⊋︀"); + html5References.addReference( 8845, "⊍"); + html5References.addReference( 8846, "⊎"); + html5References.addReference( 8846, "⊎"); + html5References.addReference( 8847, "⊏"); + html5References.addReference( 8847, "⊏"); + html5References.addReference( 8847, "⊏"); + html5References.addReference( 8847, 824, "⊏̸"); + html5References.addReference( 8848, "⊐"); + html5References.addReference( 8848, "⊐"); + html5References.addReference( 8848, "⊐"); + html5References.addReference( 8848, 824, "⊐̸"); + html5References.addReference( 8849, "⊑"); + html5References.addReference( 8849, "⊑"); + html5References.addReference( 8849, "⊑"); + html5References.addReference( 8850, "⊒"); + html5References.addReference( 8850, "⊒"); + html5References.addReference( 8850, "⊒"); + html5References.addReference( 8851, "⊓"); + html5References.addReference( 8851, "⊓"); + html5References.addReference( 8851, 65024, "⊓︀"); + html5References.addReference( 8852, "⊔"); + html5References.addReference( 8852, "⊔"); + html5References.addReference( 8852, 65024, "⊔︀"); + html5References.addReference( 8853, "⊕"); + html5References.addReference( 8853, "⊕"); + html5References.addReference( 8854, "⊖"); + html5References.addReference( 8854, "⊖"); + html5References.addReference( 8855, "⊗"); + html5References.addReference( 8855, "⊗"); + html5References.addReference( 8856, "⊘"); + html5References.addReference( 8857, "⊙"); + html5References.addReference( 8857, "⊙"); + html5References.addReference( 8858, "⊚"); + html5References.addReference( 8858, "⊚"); + html5References.addReference( 8859, "⊛"); + html5References.addReference( 8859, "⊛"); + html5References.addReference( 8861, "⊝"); + html5References.addReference( 8861, "⊝"); + html5References.addReference( 8862, "⊞"); + html5References.addReference( 8862, "⊞"); + html5References.addReference( 8863, "⊟"); + html5References.addReference( 8863, "⊟"); + html5References.addReference( 8864, "⊠"); + html5References.addReference( 8864, "⊠"); + html5References.addReference( 8865, "⊡"); + html5References.addReference( 8865, "⊡"); + html5References.addReference( 8866, "⊢"); + html5References.addReference( 8866, "⊢"); + html5References.addReference( 8867, "⊣"); + html5References.addReference( 8867, "⊣"); + html5References.addReference( 8868, "⊤"); + html5References.addReference( 8868, "⊤"); + html5References.addReference( 8869, "⊥"); + html5References.addReference( 8869, "⊥"); + html5References.addReference( 8869, "⊥"); + html5References.addReference( 8869, "⊥"); + html5References.addReference( 8871, "⊧"); + html5References.addReference( 8872, "⊨"); + html5References.addReference( 8872, "⊨"); + html5References.addReference( 8873, "⊩"); + html5References.addReference( 8874, "⊪"); + html5References.addReference( 8875, "⊫"); + html5References.addReference( 8876, "⊬"); + html5References.addReference( 8877, "⊭"); + html5References.addReference( 8878, "⊮"); + html5References.addReference( 8879, "⊯"); + html5References.addReference( 8880, "⊰"); + html5References.addReference( 8882, "⊲"); + html5References.addReference( 8882, "⊲"); + html5References.addReference( 8882, "⊲"); + html5References.addReference( 8883, "⊳"); + html5References.addReference( 8883, "⊳"); + html5References.addReference( 8883, "⊳"); + html5References.addReference( 8884, "⊴"); + html5References.addReference( 8884, "⊴"); + html5References.addReference( 8884, "⊴"); + html5References.addReference( 8884, 8402, "⊴⃒"); + html5References.addReference( 8885, "⊵"); + html5References.addReference( 8885, "⊵"); + html5References.addReference( 8885, "⊵"); + html5References.addReference( 8885, 8402, "⊵⃒"); + html5References.addReference( 8886, "⊶"); + html5References.addReference( 8887, "⊷"); + html5References.addReference( 8888, "⊸"); + html5References.addReference( 8888, "⊸"); + html5References.addReference( 8889, "⊹"); + html5References.addReference( 8890, "⊺"); + html5References.addReference( 8890, "⊺"); + html5References.addReference( 8891, "⊻"); + html5References.addReference( 8893, "⊽"); + html5References.addReference( 8894, "⊾"); + html5References.addReference( 8895, "⊿"); + html5References.addReference( 8896, "⋀"); + html5References.addReference( 8896, "⋀"); + html5References.addReference( 8896, "⋀"); + html5References.addReference( 8897, "⋁"); + html5References.addReference( 8897, "⋁"); + html5References.addReference( 8897, "⋁"); + html5References.addReference( 8898, "⋂"); + html5References.addReference( 8898, "⋂"); + html5References.addReference( 8898, "⋂"); + html5References.addReference( 8899, "⋃"); + html5References.addReference( 8899, "⋃"); + html5References.addReference( 8899, "⋃"); + html5References.addReference( 8900, "⋄"); + html5References.addReference( 8900, "⋄"); + html5References.addReference( 8900, "⋄"); + html5References.addReference( 8901, "⋅"); + html5References.addReference( 8902, "⋆"); + html5References.addReference( 8902, "⋆"); + html5References.addReference( 8903, "⋇"); + html5References.addReference( 8903, "⋇"); + html5References.addReference( 8904, "⋈"); + html5References.addReference( 8905, "⋉"); + html5References.addReference( 8906, "⋊"); + html5References.addReference( 8907, "⋋"); + html5References.addReference( 8907, "⋋"); + html5References.addReference( 8908, "⋌"); + html5References.addReference( 8908, "⋌"); + html5References.addReference( 8909, "⋍"); + html5References.addReference( 8909, "⋍"); + html5References.addReference( 8910, "⋎"); + html5References.addReference( 8910, "⋎"); + html5References.addReference( 8911, "⋏"); + html5References.addReference( 8911, "⋏"); + html5References.addReference( 8912, "⋐"); + html5References.addReference( 8912, "⋐"); + html5References.addReference( 8913, "⋑"); + html5References.addReference( 8913, "⋑"); + html5References.addReference( 8914, "⋒"); + html5References.addReference( 8915, "⋓"); + html5References.addReference( 8916, "⋔"); + html5References.addReference( 8916, "⋔"); + html5References.addReference( 8917, "⋕"); + html5References.addReference( 8918, "⋖"); + html5References.addReference( 8918, "⋖"); + html5References.addReference( 8919, "⋗"); + html5References.addReference( 8919, "⋗"); + html5References.addReference( 8920, "⋘"); + html5References.addReference( 8920, 824, "⋘̸"); + html5References.addReference( 8921, "⋙"); + html5References.addReference( 8921, "⋙"); + html5References.addReference( 8921, 824, "⋙̸"); + html5References.addReference( 8922, "⋚"); + html5References.addReference( 8922, "⋚"); + html5References.addReference( 8922, "⋚"); + html5References.addReference( 8922, 65024, "⋚︀"); + html5References.addReference( 8923, "⋛"); + html5References.addReference( 8923, "⋛"); + html5References.addReference( 8923, "⋛"); + html5References.addReference( 8923, 65024, "⋛︀"); + html5References.addReference( 8926, "⋞"); + html5References.addReference( 8926, "⋞"); + html5References.addReference( 8927, "⋟"); + html5References.addReference( 8927, "⋟"); + html5References.addReference( 8928, "⋠"); + html5References.addReference( 8928, "⋠"); + html5References.addReference( 8929, "⋡"); + html5References.addReference( 8929, "⋡"); + html5References.addReference( 8930, "⋢"); + html5References.addReference( 8930, "⋢"); + html5References.addReference( 8931, "⋣"); + html5References.addReference( 8931, "⋣"); + html5References.addReference( 8934, "⋦"); + html5References.addReference( 8935, "⋧"); + html5References.addReference( 8936, "⋨"); + html5References.addReference( 8936, "⋨"); + html5References.addReference( 8937, "⋩"); + html5References.addReference( 8937, "⋩"); + html5References.addReference( 8938, "⋪"); + html5References.addReference( 8938, "⋪"); + html5References.addReference( 8938, "⋪"); + html5References.addReference( 8939, "⋫"); + html5References.addReference( 8939, "⋫"); + html5References.addReference( 8939, "⋫"); + html5References.addReference( 8940, "⋬"); + html5References.addReference( 8940, "⋬"); + html5References.addReference( 8940, "⋬"); + html5References.addReference( 8941, "⋭"); + html5References.addReference( 8941, "⋭"); + html5References.addReference( 8941, "⋭"); + html5References.addReference( 8942, "⋮"); + html5References.addReference( 8943, "⋯"); + html5References.addReference( 8944, "⋰"); + html5References.addReference( 8945, "⋱"); + html5References.addReference( 8946, "⋲"); + html5References.addReference( 8947, "⋳"); + html5References.addReference( 8948, "⋴"); + html5References.addReference( 8949, "⋵"); + html5References.addReference( 8949, 824, "⋵̸"); + html5References.addReference( 8950, "⋶"); + html5References.addReference( 8951, "⋷"); + html5References.addReference( 8953, "⋹"); + html5References.addReference( 8953, 824, "⋹̸"); + html5References.addReference( 8954, "⋺"); + html5References.addReference( 8955, "⋻"); + html5References.addReference( 8956, "⋼"); + html5References.addReference( 8957, "⋽"); + html5References.addReference( 8958, "⋾"); + html5References.addReference( 8965, "⌅"); + html5References.addReference( 8965, "⌅"); + html5References.addReference( 8966, "⌆"); + html5References.addReference( 8966, "⌆"); + html5References.addReference( 8968, "⌈"); + html5References.addReference( 8968, "⌈"); + html5References.addReference( 8969, "⌉"); + html5References.addReference( 8969, "⌉"); + html5References.addReference( 8970, "⌊"); + html5References.addReference( 8970, "⌊"); + html5References.addReference( 8971, "⌋"); + html5References.addReference( 8971, "⌋"); + html5References.addReference( 8972, "⌌"); + html5References.addReference( 8973, "⌍"); + html5References.addReference( 8974, "⌎"); + html5References.addReference( 8975, "⌏"); + html5References.addReference( 8976, "⌐"); + html5References.addReference( 8978, "⌒"); + html5References.addReference( 8979, "⌓"); + html5References.addReference( 8981, "⌕"); + html5References.addReference( 8982, "⌖"); + html5References.addReference( 8988, "⌜"); + html5References.addReference( 8988, "⌜"); + html5References.addReference( 8989, "⌝"); + html5References.addReference( 8989, "⌝"); + html5References.addReference( 8990, "⌞"); + html5References.addReference( 8990, "⌞"); + html5References.addReference( 8991, "⌟"); + html5References.addReference( 8991, "⌟"); + html5References.addReference( 8994, "⌢"); + html5References.addReference( 8994, "⌢"); + html5References.addReference( 8995, "⌣"); + html5References.addReference( 8995, "⌣"); + html5References.addReference( 9005, "⌭"); + html5References.addReference( 9006, "⌮"); + html5References.addReference( 9014, "⌶"); + html5References.addReference( 9021, "⌽"); + html5References.addReference( 9023, "⌿"); + html5References.addReference( 9084, "⍼"); + html5References.addReference( 9136, "⎰"); + html5References.addReference( 9136, "⎰"); + html5References.addReference( 9137, "⎱"); + html5References.addReference( 9137, "⎱"); + html5References.addReference( 9140, "⎴"); + html5References.addReference( 9140, "⎴"); + html5References.addReference( 9141, "⎵"); + html5References.addReference( 9141, "⎵"); + html5References.addReference( 9142, "⎶"); + html5References.addReference( 9180, "⏜"); + html5References.addReference( 9181, "⏝"); + html5References.addReference( 9182, "⏞"); + html5References.addReference( 9183, "⏟"); + html5References.addReference( 9186, "⏢"); + html5References.addReference( 9191, "⏧"); + html5References.addReference( 9251, "␣"); + html5References.addReference( 9416, "Ⓢ"); + html5References.addReference( 9416, "Ⓢ"); + html5References.addReference( 9472, "─"); + html5References.addReference( 9472, "─"); + html5References.addReference( 9474, "│"); + html5References.addReference( 9484, "┌"); + html5References.addReference( 9488, "┐"); + html5References.addReference( 9492, "└"); + html5References.addReference( 9496, "┘"); + html5References.addReference( 9500, "├"); + html5References.addReference( 9508, "┤"); + html5References.addReference( 9516, "┬"); + html5References.addReference( 9524, "┴"); + html5References.addReference( 9532, "┼"); + html5References.addReference( 9552, "═"); + html5References.addReference( 9553, "║"); + html5References.addReference( 9554, "╒"); + html5References.addReference( 9555, "╓"); + html5References.addReference( 9556, "╔"); + html5References.addReference( 9557, "╕"); + html5References.addReference( 9558, "╖"); + html5References.addReference( 9559, "╗"); + html5References.addReference( 9560, "╘"); + html5References.addReference( 9561, "╙"); + html5References.addReference( 9562, "╚"); + html5References.addReference( 9563, "╛"); + html5References.addReference( 9564, "╜"); + html5References.addReference( 9565, "╝"); + html5References.addReference( 9566, "╞"); + html5References.addReference( 9567, "╟"); + html5References.addReference( 9568, "╠"); + html5References.addReference( 9569, "╡"); + html5References.addReference( 9570, "╢"); + html5References.addReference( 9571, "╣"); + html5References.addReference( 9572, "╤"); + html5References.addReference( 9573, "╥"); + html5References.addReference( 9574, "╦"); + html5References.addReference( 9575, "╧"); + html5References.addReference( 9576, "╨"); + html5References.addReference( 9577, "╩"); + html5References.addReference( 9578, "╪"); + html5References.addReference( 9579, "╫"); + html5References.addReference( 9580, "╬"); + html5References.addReference( 9600, "▀"); + html5References.addReference( 9604, "▄"); + html5References.addReference( 9608, "█"); + html5References.addReference( 9617, "░"); + html5References.addReference( 9618, "▒"); + html5References.addReference( 9619, "▓"); + html5References.addReference( 9633, "□"); + html5References.addReference( 9633, "□"); + html5References.addReference( 9633, "□"); + html5References.addReference( 9642, "▪"); + html5References.addReference( 9642, "▪"); + html5References.addReference( 9642, "▪"); + html5References.addReference( 9642, "▪"); + html5References.addReference( 9643, "▫"); + html5References.addReference( 9645, "▭"); + html5References.addReference( 9646, "▮"); + html5References.addReference( 9649, "▱"); + html5References.addReference( 9651, "△"); + html5References.addReference( 9651, "△"); + html5References.addReference( 9652, "▴"); + html5References.addReference( 9652, "▴"); + html5References.addReference( 9653, "▵"); + html5References.addReference( 9653, "▵"); + html5References.addReference( 9656, "▸"); + html5References.addReference( 9656, "▸"); + html5References.addReference( 9657, "▹"); + html5References.addReference( 9657, "▹"); + html5References.addReference( 9661, "▽"); + html5References.addReference( 9661, "▽"); + html5References.addReference( 9662, "▾"); + html5References.addReference( 9662, "▾"); + html5References.addReference( 9663, "▿"); + html5References.addReference( 9663, "▿"); + html5References.addReference( 9666, "◂"); + html5References.addReference( 9666, "◂"); + html5References.addReference( 9667, "◃"); + html5References.addReference( 9667, "◃"); + html5References.addReference( 9674, "◊"); + html5References.addReference( 9674, "◊"); + html5References.addReference( 9675, "○"); + html5References.addReference( 9708, "◬"); + html5References.addReference( 9711, "◯"); + html5References.addReference( 9711, "◯"); + html5References.addReference( 9720, "◸"); + html5References.addReference( 9721, "◹"); + html5References.addReference( 9722, "◺"); + html5References.addReference( 9723, "◻"); + html5References.addReference( 9724, "◼"); + html5References.addReference( 9733, "★"); + html5References.addReference( 9733, "★"); + html5References.addReference( 9734, "☆"); + html5References.addReference( 9742, "☎"); + html5References.addReference( 9792, "♀"); + html5References.addReference( 9794, "♂"); + html5References.addReference( 9824, "♠"); + html5References.addReference( 9824, "♠"); + html5References.addReference( 9827, "♣"); + html5References.addReference( 9827, "♣"); + html5References.addReference( 9829, "♥"); + html5References.addReference( 9829, "♥"); + html5References.addReference( 9830, "♦"); + html5References.addReference( 9830, "♦"); + html5References.addReference( 9834, "♪"); + html5References.addReference( 9837, "♭"); + html5References.addReference( 9838, "♮"); + html5References.addReference( 9838, "♮"); + html5References.addReference( 9839, "♯"); + html5References.addReference( 10003, "✓"); + html5References.addReference( 10003, "✓"); + html5References.addReference( 10007, "✗"); + html5References.addReference( 10016, "✠"); + html5References.addReference( 10016, "✠"); + html5References.addReference( 10038, "✶"); + html5References.addReference( 10072, "❘"); + html5References.addReference( 10098, "❲"); + html5References.addReference( 10099, "❳"); + html5References.addReference( 10184, "⟈"); + html5References.addReference( 10185, "⟉"); + html5References.addReference( 10214, "⟦"); + html5References.addReference( 10214, "⟦"); + html5References.addReference( 10215, "⟧"); + html5References.addReference( 10215, "⟧"); + html5References.addReference( 10216, "⟨"); + html5References.addReference( 10216, "⟨"); + html5References.addReference( 10216, "⟨"); + html5References.addReference( 10217, "⟩"); + html5References.addReference( 10217, "⟩"); + html5References.addReference( 10217, "⟩"); + html5References.addReference( 10218, "⟪"); + html5References.addReference( 10219, "⟫"); + html5References.addReference( 10220, "⟬"); + html5References.addReference( 10221, "⟭"); + html5References.addReference( 10229, "⟵"); + html5References.addReference( 10229, "⟵"); + html5References.addReference( 10229, "⟵"); + html5References.addReference( 10230, "⟶"); + html5References.addReference( 10230, "⟶"); + html5References.addReference( 10230, "⟶"); + html5References.addReference( 10231, "⟷"); + html5References.addReference( 10231, "⟷"); + html5References.addReference( 10231, "⟷"); + html5References.addReference( 10232, "⟸"); + html5References.addReference( 10232, "⟸"); + html5References.addReference( 10232, "⟸"); + html5References.addReference( 10233, "⟹"); + html5References.addReference( 10233, "⟹"); + html5References.addReference( 10233, "⟹"); + html5References.addReference( 10234, "⟺"); + html5References.addReference( 10234, "⟺"); + html5References.addReference( 10234, "⟺"); + html5References.addReference( 10236, "⟼"); + html5References.addReference( 10236, "⟼"); + html5References.addReference( 10239, "⟿"); + html5References.addReference( 10498, "⤂"); + html5References.addReference( 10499, "⤃"); + html5References.addReference( 10500, "⤄"); + html5References.addReference( 10501, "⤅"); + html5References.addReference( 10508, "⤌"); + html5References.addReference( 10509, "⤍"); + html5References.addReference( 10509, "⤍"); + html5References.addReference( 10510, "⤎"); + html5References.addReference( 10511, "⤏"); + html5References.addReference( 10511, "⤏"); + html5References.addReference( 10512, "⤐"); + html5References.addReference( 10512, "⤐"); + html5References.addReference( 10513, "⤑"); + html5References.addReference( 10514, "⤒"); + html5References.addReference( 10515, "⤓"); + html5References.addReference( 10518, "⤖"); + html5References.addReference( 10521, "⤙"); + html5References.addReference( 10522, "⤚"); + html5References.addReference( 10523, "⤛"); + html5References.addReference( 10524, "⤜"); + html5References.addReference( 10525, "⤝"); + html5References.addReference( 10526, "⤞"); + html5References.addReference( 10527, "⤟"); + html5References.addReference( 10528, "⤠"); + html5References.addReference( 10531, "⤣"); + html5References.addReference( 10532, "⤤"); + html5References.addReference( 10533, "⤥"); + html5References.addReference( 10533, "⤥"); + html5References.addReference( 10534, "⤦"); + html5References.addReference( 10534, "⤦"); + html5References.addReference( 10535, "⤧"); + html5References.addReference( 10536, "⤨"); + html5References.addReference( 10536, "⤨"); + html5References.addReference( 10537, "⤩"); + html5References.addReference( 10537, "⤩"); + html5References.addReference( 10538, "⤪"); + html5References.addReference( 10547, "⤳"); + html5References.addReference( 10547, 824, "⤳̸"); + html5References.addReference( 10549, "⤵"); + html5References.addReference( 10550, "⤶"); + html5References.addReference( 10551, "⤷"); + html5References.addReference( 10552, "⤸"); + html5References.addReference( 10553, "⤹"); + html5References.addReference( 10556, "⤼"); + html5References.addReference( 10557, "⤽"); + html5References.addReference( 10565, "⥅"); + html5References.addReference( 10568, "⥈"); + html5References.addReference( 10569, "⥉"); + html5References.addReference( 10570, "⥊"); + html5References.addReference( 10571, "⥋"); + html5References.addReference( 10574, "⥎"); + html5References.addReference( 10575, "⥏"); + html5References.addReference( 10576, "⥐"); + html5References.addReference( 10577, "⥑"); + html5References.addReference( 10578, "⥒"); + html5References.addReference( 10579, "⥓"); + html5References.addReference( 10580, "⥔"); + html5References.addReference( 10581, "⥕"); + html5References.addReference( 10582, "⥖"); + html5References.addReference( 10583, "⥗"); + html5References.addReference( 10584, "⥘"); + html5References.addReference( 10585, "⥙"); + html5References.addReference( 10586, "⥚"); + html5References.addReference( 10587, "⥛"); + html5References.addReference( 10588, "⥜"); + html5References.addReference( 10589, "⥝"); + html5References.addReference( 10590, "⥞"); + html5References.addReference( 10591, "⥟"); + html5References.addReference( 10592, "⥠"); + html5References.addReference( 10593, "⥡"); + html5References.addReference( 10594, "⥢"); + html5References.addReference( 10595, "⥣"); + html5References.addReference( 10596, "⥤"); + html5References.addReference( 10597, "⥥"); + html5References.addReference( 10598, "⥦"); + html5References.addReference( 10599, "⥧"); + html5References.addReference( 10600, "⥨"); + html5References.addReference( 10601, "⥩"); + html5References.addReference( 10602, "⥪"); + html5References.addReference( 10603, "⥫"); + html5References.addReference( 10604, "⥬"); + html5References.addReference( 10605, "⥭"); + html5References.addReference( 10606, "⥮"); + html5References.addReference( 10606, "⥮"); + html5References.addReference( 10607, "⥯"); + html5References.addReference( 10607, "⥯"); + html5References.addReference( 10608, "⥰"); + html5References.addReference( 10609, "⥱"); + html5References.addReference( 10610, "⥲"); + html5References.addReference( 10611, "⥳"); + html5References.addReference( 10612, "⥴"); + html5References.addReference( 10613, "⥵"); + html5References.addReference( 10614, "⥶"); + html5References.addReference( 10616, "⥸"); + html5References.addReference( 10617, "⥹"); + html5References.addReference( 10619, "⥻"); + html5References.addReference( 10620, "⥼"); + html5References.addReference( 10621, "⥽"); + html5References.addReference( 10622, "⥾"); + html5References.addReference( 10623, "⥿"); + html5References.addReference( 10629, "⦅"); + html5References.addReference( 10630, "⦆"); + html5References.addReference( 10635, "⦋"); + html5References.addReference( 10636, "⦌"); + html5References.addReference( 10637, "⦍"); + html5References.addReference( 10638, "⦎"); + html5References.addReference( 10639, "⦏"); + html5References.addReference( 10640, "⦐"); + html5References.addReference( 10641, "⦑"); + html5References.addReference( 10642, "⦒"); + html5References.addReference( 10643, "⦓"); + html5References.addReference( 10644, "⦔"); + html5References.addReference( 10645, "⦕"); + html5References.addReference( 10646, "⦖"); + html5References.addReference( 10650, "⦚"); + html5References.addReference( 10652, "⦜"); + html5References.addReference( 10653, "⦝"); + html5References.addReference( 10660, "⦤"); + html5References.addReference( 10661, "⦥"); + html5References.addReference( 10662, "⦦"); + html5References.addReference( 10663, "⦧"); + html5References.addReference( 10664, "⦨"); + html5References.addReference( 10665, "⦩"); + html5References.addReference( 10666, "⦪"); + html5References.addReference( 10667, "⦫"); + html5References.addReference( 10668, "⦬"); + html5References.addReference( 10669, "⦭"); + html5References.addReference( 10670, "⦮"); + html5References.addReference( 10671, "⦯"); + html5References.addReference( 10672, "⦰"); + html5References.addReference( 10673, "⦱"); + html5References.addReference( 10674, "⦲"); + html5References.addReference( 10675, "⦳"); + html5References.addReference( 10676, "⦴"); + html5References.addReference( 10677, "⦵"); + html5References.addReference( 10678, "⦶"); + html5References.addReference( 10679, "⦷"); + html5References.addReference( 10681, "⦹"); + html5References.addReference( 10683, "⦻"); + html5References.addReference( 10684, "⦼"); + html5References.addReference( 10686, "⦾"); + html5References.addReference( 10687, "⦿"); + html5References.addReference( 10688, "⧀"); + html5References.addReference( 10689, "⧁"); + html5References.addReference( 10690, "⧂"); + html5References.addReference( 10691, "⧃"); + html5References.addReference( 10692, "⧄"); + html5References.addReference( 10693, "⧅"); + html5References.addReference( 10697, "⧉"); + html5References.addReference( 10701, "⧍"); + html5References.addReference( 10702, "⧎"); + html5References.addReference( 10703, "⧏"); + html5References.addReference( 10703, 824, "⧏̸"); + html5References.addReference( 10704, "⧐"); + html5References.addReference( 10704, 824, "⧐̸"); + html5References.addReference( 10716, "⧜"); + html5References.addReference( 10717, "⧝"); + html5References.addReference( 10718, "⧞"); + html5References.addReference( 10723, "⧣"); + html5References.addReference( 10724, "⧤"); + html5References.addReference( 10725, "⧥"); + html5References.addReference( 10731, "⧫"); + html5References.addReference( 10731, "⧫"); + html5References.addReference( 10740, "⧴"); + html5References.addReference( 10742, "⧶"); + html5References.addReference( 10752, "⨀"); + html5References.addReference( 10752, "⨀"); + html5References.addReference( 10753, "⨁"); + html5References.addReference( 10753, "⨁"); + html5References.addReference( 10754, "⨂"); + html5References.addReference( 10754, "⨂"); + html5References.addReference( 10756, "⨄"); + html5References.addReference( 10756, "⨄"); + html5References.addReference( 10758, "⨆"); + html5References.addReference( 10758, "⨆"); + html5References.addReference( 10764, "⨌"); + html5References.addReference( 10764, "⨌"); + html5References.addReference( 10765, "⨍"); + html5References.addReference( 10768, "⨐"); + html5References.addReference( 10769, "⨑"); + html5References.addReference( 10770, "⨒"); + html5References.addReference( 10771, "⨓"); + html5References.addReference( 10772, "⨔"); + html5References.addReference( 10773, "⨕"); + html5References.addReference( 10774, "⨖"); + html5References.addReference( 10775, "⨗"); + html5References.addReference( 10786, "⨢"); + html5References.addReference( 10787, "⨣"); + html5References.addReference( 10788, "⨤"); + html5References.addReference( 10789, "⨥"); + html5References.addReference( 10790, "⨦"); + html5References.addReference( 10791, "⨧"); + html5References.addReference( 10793, "⨩"); + html5References.addReference( 10794, "⨪"); + html5References.addReference( 10797, "⨭"); + html5References.addReference( 10798, "⨮"); + html5References.addReference( 10799, "⨯"); + html5References.addReference( 10800, "⨰"); + html5References.addReference( 10801, "⨱"); + html5References.addReference( 10803, "⨳"); + html5References.addReference( 10804, "⨴"); + html5References.addReference( 10805, "⨵"); + html5References.addReference( 10806, "⨶"); + html5References.addReference( 10807, "⨷"); + html5References.addReference( 10808, "⨸"); + html5References.addReference( 10809, "⨹"); + html5References.addReference( 10810, "⨺"); + html5References.addReference( 10811, "⨻"); + html5References.addReference( 10812, "⨼"); + html5References.addReference( 10812, "⨼"); + html5References.addReference( 10815, "⨿"); + html5References.addReference( 10816, "⩀"); + html5References.addReference( 10818, "⩂"); + html5References.addReference( 10819, "⩃"); + html5References.addReference( 10820, "⩄"); + html5References.addReference( 10821, "⩅"); + html5References.addReference( 10822, "⩆"); + html5References.addReference( 10823, "⩇"); + html5References.addReference( 10824, "⩈"); + html5References.addReference( 10825, "⩉"); + html5References.addReference( 10826, "⩊"); + html5References.addReference( 10827, "⩋"); + html5References.addReference( 10828, "⩌"); + html5References.addReference( 10829, "⩍"); + html5References.addReference( 10832, "⩐"); + html5References.addReference( 10835, "⩓"); + html5References.addReference( 10836, "⩔"); + html5References.addReference( 10837, "⩕"); + html5References.addReference( 10838, "⩖"); + html5References.addReference( 10839, "⩗"); + html5References.addReference( 10840, "⩘"); + html5References.addReference( 10842, "⩚"); + html5References.addReference( 10843, "⩛"); + html5References.addReference( 10844, "⩜"); + html5References.addReference( 10845, "⩝"); + html5References.addReference( 10847, "⩟"); + html5References.addReference( 10854, "⩦"); + html5References.addReference( 10858, "⩪"); + html5References.addReference( 10861, "⩭"); + html5References.addReference( 10861, 824, "⩭̸"); + html5References.addReference( 10862, "⩮"); + html5References.addReference( 10863, "⩯"); + html5References.addReference( 10864, "⩰"); + html5References.addReference( 10864, 824, "⩰̸"); + html5References.addReference( 10865, "⩱"); + html5References.addReference( 10866, "⩲"); + html5References.addReference( 10867, "⩳"); + html5References.addReference( 10868, "⩴"); + html5References.addReference( 10869, "⩵"); + html5References.addReference( 10871, "⩷"); + html5References.addReference( 10871, "⩷"); + html5References.addReference( 10872, "⩸"); + html5References.addReference( 10873, "⩹"); + html5References.addReference( 10874, "⩺"); + html5References.addReference( 10875, "⩻"); + html5References.addReference( 10876, "⩼"); + html5References.addReference( 10877, "⩽"); + html5References.addReference( 10877, "⩽"); + html5References.addReference( 10877, "⩽"); + html5References.addReference( 10877, 824, "⩽̸"); + html5References.addReference( 10877, 824, "⩽̸"); + html5References.addReference( 10877, 824, "⩽̸"); + html5References.addReference( 10878, "⩾"); + html5References.addReference( 10878, "⩾"); + html5References.addReference( 10878, "⩾"); + html5References.addReference( 10878, 824, "⩾̸"); + html5References.addReference( 10878, 824, "⩾̸"); + html5References.addReference( 10878, 824, "⩾̸"); + html5References.addReference( 10879, "⩿"); + html5References.addReference( 10880, "⪀"); + html5References.addReference( 10881, "⪁"); + html5References.addReference( 10882, "⪂"); + html5References.addReference( 10883, "⪃"); + html5References.addReference( 10884, "⪄"); + html5References.addReference( 10885, "⪅"); + html5References.addReference( 10885, "⪅"); + html5References.addReference( 10886, "⪆"); + html5References.addReference( 10886, "⪆"); + html5References.addReference( 10887, "⪇"); + html5References.addReference( 10887, "⪇"); + html5References.addReference( 10888, "⪈"); + html5References.addReference( 10888, "⪈"); + html5References.addReference( 10889, "⪉"); + html5References.addReference( 10889, "⪉"); + html5References.addReference( 10890, "⪊"); + html5References.addReference( 10890, "⪊"); + html5References.addReference( 10891, "⪋"); + html5References.addReference( 10891, "⪋"); + html5References.addReference( 10892, "⪌"); + html5References.addReference( 10892, "⪌"); + html5References.addReference( 10893, "⪍"); + html5References.addReference( 10894, "⪎"); + html5References.addReference( 10895, "⪏"); + html5References.addReference( 10896, "⪐"); + html5References.addReference( 10897, "⪑"); + html5References.addReference( 10898, "⪒"); + html5References.addReference( 10899, "⪓"); + html5References.addReference( 10900, "⪔"); + html5References.addReference( 10901, "⪕"); + html5References.addReference( 10901, "⪕"); + html5References.addReference( 10902, "⪖"); + html5References.addReference( 10902, "⪖"); + html5References.addReference( 10903, "⪗"); + html5References.addReference( 10904, "⪘"); + html5References.addReference( 10905, "⪙"); + html5References.addReference( 10906, "⪚"); + html5References.addReference( 10909, "⪝"); + html5References.addReference( 10910, "⪞"); + html5References.addReference( 10911, "⪟"); + html5References.addReference( 10912, "⪠"); + html5References.addReference( 10913, "⪡"); + html5References.addReference( 10913, 824, "⪡̸"); + html5References.addReference( 10914, "⪢"); + html5References.addReference( 10914, 824, "⪢̸"); + html5References.addReference( 10916, "⪤"); + html5References.addReference( 10917, "⪥"); + html5References.addReference( 10918, "⪦"); + html5References.addReference( 10919, "⪧"); + html5References.addReference( 10920, "⪨"); + html5References.addReference( 10921, "⪩"); + html5References.addReference( 10922, "⪪"); + html5References.addReference( 10923, "⪫"); + html5References.addReference( 10924, "⪬"); + html5References.addReference( 10924, 65024, "⪬︀"); + html5References.addReference( 10925, "⪭"); + html5References.addReference( 10925, 65024, "⪭︀"); + html5References.addReference( 10926, "⪮"); + html5References.addReference( 10927, "⪯"); + html5References.addReference( 10927, "⪯"); + html5References.addReference( 10927, "⪯"); + html5References.addReference( 10927, 824, "⪯̸"); + html5References.addReference( 10927, 824, "⪯̸"); + html5References.addReference( 10927, 824, "⪯̸"); + html5References.addReference( 10928, "⪰"); + html5References.addReference( 10928, "⪰"); + html5References.addReference( 10928, "⪰"); + html5References.addReference( 10928, 824, "⪰̸"); + html5References.addReference( 10928, 824, "⪰̸"); + html5References.addReference( 10928, 824, "⪰̸"); + html5References.addReference( 10931, "⪳"); + html5References.addReference( 10932, "⪴"); + html5References.addReference( 10933, "⪵"); + html5References.addReference( 10933, "⪵"); + html5References.addReference( 10934, "⪶"); + html5References.addReference( 10934, "⪶"); + html5References.addReference( 10935, "⪷"); + html5References.addReference( 10935, "⪷"); + html5References.addReference( 10936, "⪸"); + html5References.addReference( 10936, "⪸"); + html5References.addReference( 10937, "⪹"); + html5References.addReference( 10937, "⪹"); + html5References.addReference( 10938, "⪺"); + html5References.addReference( 10938, "⪺"); + html5References.addReference( 10939, "⪻"); + html5References.addReference( 10940, "⪼"); + html5References.addReference( 10941, "⪽"); + html5References.addReference( 10942, "⪾"); + html5References.addReference( 10943, "⪿"); + html5References.addReference( 10944, "⫀"); + html5References.addReference( 10945, "⫁"); + html5References.addReference( 10946, "⫂"); + html5References.addReference( 10947, "⫃"); + html5References.addReference( 10948, "⫄"); + html5References.addReference( 10949, "⫅"); + html5References.addReference( 10949, "⫅"); + html5References.addReference( 10949, 824, "⫅̸"); + html5References.addReference( 10949, 824, "⫅̸"); + html5References.addReference( 10950, "⫆"); + html5References.addReference( 10950, "⫆"); + html5References.addReference( 10950, 824, "⫆̸"); + html5References.addReference( 10950, 824, "⫆̸"); + html5References.addReference( 10951, "⫇"); + html5References.addReference( 10952, "⫈"); + html5References.addReference( 10955, "⫋"); + html5References.addReference( 10955, "⫋"); + html5References.addReference( 10955, 65024, "⫋︀"); + html5References.addReference( 10955, 65024, "⫋︀"); + html5References.addReference( 10956, "⫌"); + html5References.addReference( 10956, "⫌"); + html5References.addReference( 10956, 65024, "⫌︀"); + html5References.addReference( 10956, 65024, "⫌︀"); + html5References.addReference( 10959, "⫏"); + html5References.addReference( 10960, "⫐"); + html5References.addReference( 10961, "⫑"); + html5References.addReference( 10962, "⫒"); + html5References.addReference( 10963, "⫓"); + html5References.addReference( 10964, "⫔"); + html5References.addReference( 10965, "⫕"); + html5References.addReference( 10966, "⫖"); + html5References.addReference( 10967, "⫗"); + html5References.addReference( 10968, "⫘"); + html5References.addReference( 10969, "⫙"); + html5References.addReference( 10970, "⫚"); + html5References.addReference( 10971, "⫛"); + html5References.addReference( 10980, "⫤"); + html5References.addReference( 10980, "⫤"); + html5References.addReference( 10982, "⫦"); + html5References.addReference( 10983, "⫧"); + html5References.addReference( 10984, "⫨"); + html5References.addReference( 10985, "⫩"); + html5References.addReference( 10987, "⫫"); + html5References.addReference( 10988, "⫬"); + html5References.addReference( 10989, "⫭"); + html5References.addReference( 10990, "⫮"); + html5References.addReference( 10991, "⫯"); + html5References.addReference( 10992, "⫰"); + html5References.addReference( 10993, "⫱"); + html5References.addReference( 10994, "⫲"); + html5References.addReference( 10995, "⫳"); + html5References.addReference( 11005, "⫽"); + html5References.addReference( 11005, 8421, "⫽⃥"); + html5References.addReference( 64256, "ff"); + html5References.addReference( 64257, "fi"); + html5References.addReference( 64258, "fl"); + html5References.addReference( 64259, "ffi"); + html5References.addReference( 64260, "ffl"); + html5References.addReference(119964, "𝒜"); + html5References.addReference(119966, "𝒞"); + html5References.addReference(119967, "𝒟"); + html5References.addReference(119970, "𝒢"); + html5References.addReference(119973, "𝒥"); + html5References.addReference(119974, "𝒦"); + html5References.addReference(119977, "𝒩"); + html5References.addReference(119978, "𝒪"); + html5References.addReference(119979, "𝒫"); + html5References.addReference(119980, "𝒬"); + html5References.addReference(119982, "𝒮"); + html5References.addReference(119983, "𝒯"); + html5References.addReference(119984, "𝒰"); + html5References.addReference(119985, "𝒱"); + html5References.addReference(119986, "𝒲"); + html5References.addReference(119987, "𝒳"); + html5References.addReference(119988, "𝒴"); + html5References.addReference(119989, "𝒵"); + html5References.addReference(119990, "𝒶"); + html5References.addReference(119991, "𝒷"); + html5References.addReference(119992, "𝒸"); + html5References.addReference(119993, "𝒹"); + html5References.addReference(119995, "𝒻"); + html5References.addReference(119997, "𝒽"); + html5References.addReference(119998, "𝒾"); + html5References.addReference(119999, "𝒿"); + html5References.addReference(120000, "𝓀"); + html5References.addReference(120001, "𝓁"); + html5References.addReference(120002, "𝓂"); + html5References.addReference(120003, "𝓃"); + html5References.addReference(120005, "𝓅"); + html5References.addReference(120006, "𝓆"); + html5References.addReference(120007, "𝓇"); + html5References.addReference(120008, "𝓈"); + html5References.addReference(120009, "𝓉"); + html5References.addReference(120010, "𝓊"); + html5References.addReference(120011, "𝓋"); + html5References.addReference(120012, "𝓌"); + html5References.addReference(120013, "𝓍"); + html5References.addReference(120014, "𝓎"); + html5References.addReference(120015, "𝓏"); + html5References.addReference(120068, "𝔄"); + html5References.addReference(120069, "𝔅"); + html5References.addReference(120071, "𝔇"); + html5References.addReference(120072, "𝔈"); + html5References.addReference(120073, "𝔉"); + html5References.addReference(120074, "𝔊"); + html5References.addReference(120077, "𝔍"); + html5References.addReference(120078, "𝔎"); + html5References.addReference(120079, "𝔏"); + html5References.addReference(120080, "𝔐"); + html5References.addReference(120081, "𝔑"); + html5References.addReference(120082, "𝔒"); + html5References.addReference(120083, "𝔓"); + html5References.addReference(120084, "𝔔"); + html5References.addReference(120086, "𝔖"); + html5References.addReference(120087, "𝔗"); + html5References.addReference(120088, "𝔘"); + html5References.addReference(120089, "𝔙"); + html5References.addReference(120090, "𝔚"); + html5References.addReference(120091, "𝔛"); + html5References.addReference(120092, "𝔜"); + html5References.addReference(120094, "𝔞"); + html5References.addReference(120095, "𝔟"); + html5References.addReference(120096, "𝔠"); + html5References.addReference(120097, "𝔡"); + html5References.addReference(120098, "𝔢"); + html5References.addReference(120099, "𝔣"); + html5References.addReference(120100, "𝔤"); + html5References.addReference(120101, "𝔥"); + html5References.addReference(120102, "𝔦"); + html5References.addReference(120103, "𝔧"); + html5References.addReference(120104, "𝔨"); + html5References.addReference(120105, "𝔩"); + html5References.addReference(120106, "𝔪"); + html5References.addReference(120107, "𝔫"); + html5References.addReference(120108, "𝔬"); + html5References.addReference(120109, "𝔭"); + html5References.addReference(120110, "𝔮"); + html5References.addReference(120111, "𝔯"); + html5References.addReference(120112, "𝔰"); + html5References.addReference(120113, "𝔱"); + html5References.addReference(120114, "𝔲"); + html5References.addReference(120115, "𝔳"); + html5References.addReference(120116, "𝔴"); + html5References.addReference(120117, "𝔵"); + html5References.addReference(120118, "𝔶"); + html5References.addReference(120119, "𝔷"); + html5References.addReference(120120, "𝔸"); + html5References.addReference(120121, "𝔹"); + html5References.addReference(120123, "𝔻"); + html5References.addReference(120124, "𝔼"); + html5References.addReference(120125, "𝔽"); + html5References.addReference(120126, "𝔾"); + html5References.addReference(120128, "𝕀"); + html5References.addReference(120129, "𝕁"); + html5References.addReference(120130, "𝕂"); + html5References.addReference(120131, "𝕃"); + html5References.addReference(120132, "𝕄"); + html5References.addReference(120134, "𝕆"); + html5References.addReference(120138, "𝕊"); + html5References.addReference(120139, "𝕋"); + html5References.addReference(120140, "𝕌"); + html5References.addReference(120141, "𝕍"); + html5References.addReference(120142, "𝕎"); + html5References.addReference(120143, "𝕏"); + html5References.addReference(120144, "𝕐"); + html5References.addReference(120146, "𝕒"); + html5References.addReference(120147, "𝕓"); + html5References.addReference(120148, "𝕔"); + html5References.addReference(120149, "𝕕"); + html5References.addReference(120150, "𝕖"); + html5References.addReference(120151, "𝕗"); + html5References.addReference(120152, "𝕘"); + html5References.addReference(120153, "𝕙"); + html5References.addReference(120154, "𝕚"); + html5References.addReference(120155, "𝕛"); + html5References.addReference(120156, "𝕜"); + html5References.addReference(120157, "𝕝"); + html5References.addReference(120158, "𝕞"); + html5References.addReference(120159, "𝕟"); + html5References.addReference(120160, "𝕠"); + html5References.addReference(120161, "𝕡"); + html5References.addReference(120162, "𝕢"); + html5References.addReference(120163, "𝕣"); + html5References.addReference(120164, "𝕤"); + html5References.addReference(120165, "𝕥"); + html5References.addReference(120166, "𝕦"); + html5References.addReference(120167, "𝕧"); + html5References.addReference(120168, "𝕨"); + html5References.addReference(120169, "𝕩"); + html5References.addReference(120170, "𝕪"); + html5References.addReference(120171, "𝕫"); + + /* + * Initialization of escape levels. + * Defined levels : + * + * - Level 0 : Only markup-significant characters except the apostrophe (') + * - Level 1 : Only markup-significant characters (including the apostrophe) + * - Level 2 : Markup-significant characters plus all non-ASCII + * - Level 3 : All non-alphanumeric characters + * - Level 4 : All characters + */ + final byte[] escapeLevels = new byte[0x7f + 2]; + Arrays.fill(escapeLevels, (byte)3); + for (char c = 'A'; c <= 'Z'; c++) { + escapeLevels[c] = 4; + } + for (char c = 'a'; c <= 'z'; c++) { + escapeLevels[c] = 4; + } + for (char c = '0'; c <= '9'; c++) { + escapeLevels[c] = 4; + } + escapeLevels['\''] = 1; + escapeLevels['"'] = 0; + escapeLevels['<'] = 0; + escapeLevels['>'] = 0; + escapeLevels['&'] = 0; + escapeLevels[0x7f + 1] = 2; + + + return new HtmlEscapeSymbols(html5References, escapeLevels); + + } + + + + private Html5EscapeSymbolsInitializer() { + super(); + } + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscape.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscape.java new file mode 100755 index 0000000..7d1cd8c --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscape.java @@ -0,0 +1,1283 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +/** + * <p> + * Utility class for performing HTML escape/unescape operations. + * </p> + * + * <strong><u>Configuration of escape/unescape operations</u></strong> + * + * <p> + * <strong>Escape</strong> operations can be (optionally) configured by means of: + * </p> + * <ul> + * <li><em>Level</em>, which defines how deep the escape operation must be (what + * chars are to be considered eligible for escaping, depending on the specific + * needs of the scenario). Its values are defined by the {@link org.unbescape.html.HtmlEscapeLevel} + * enum.</li> + * <li><em>Type</em>, which defines whether escaping should be performed by means of NCRs + * (Named Character References), by means of decimal/hexadecimal numerical references, + * using the HTML5 or the HTML 4 NCR set, etc. Its values are defined by the + * {@link org.unbescape.html.HtmlEscapeType} enum.</li> + * </ul> + * <p> + * <strong>Unescape</strong> operations need no configuration parameters. Unescape operations + * will always perform <em>complete</em> unescape of NCRs (whole HTML5 set supported), decimal + * and hexadecimal references. + * </p> + * + * <strong><u>Features</u></strong> + * + * <p> + * Specific features of the HTML escape/unescape operations performed by means of this class: + * </p> + * <ul> + * <li>Whole HTML5 NCR (Named Character Reference) set supported, if required: + * <tt>&rsqb;</tt>,<tt>&NewLine;</tt>, etc. (HTML 4 set available too).</li> + * <li>Mixed named and numerical (decimal or hexa) character references supported.</li> + * <li>Ability to default to numerical (decimal or hexa) references when an applicable NCR does not exist + * (depending on the selected operation <em>level</em>).</li> + * <li>Support for the whole Unicode character set: <tt>\u0000</tt> to <tt>\u10FFFF</tt>, including + * characters not representable by only one <tt>char</tt> in Java (<tt>>\uFFFF</tt>).</li> + * <li>Support for unescape of double-char NCRs in HTML5: <tt>'&fjlig;'</tt> → <tt>'fj'</tt>.</li> + * <li>Support for a set of HTML5 unescape <em>tweaks</em> included in the HTML5 specification: + * <ul> + * <li>Unescape of numerical character references not ending in semi-colon + * (e.g. <tt>'&#x23ac'</tt>).</li> + * <li>Unescape of specific NCRs not ending in semi-colon (e.g. <tt>'&aacute'</tt>).</li> + * <li>Unescape of specific numerical character references wrongly specified by their Windows-1252 + * codepage code instead of the Unicode one (e.g. <tt>'&#x80;'</tt> for '€' + * (<tt>'&euro;'</tt>) instead of <tt>'&#x20ac;'</tt>).</li> + * </ul> + * </li> + * </ul> + * + * <strong><u>Input/Output</u></strong> + * + * <p> + * There are four different input/output modes that can be used in escape/unescape operations: + * </p> + * <ul> + * <li><em><tt>String</tt> input, <tt>String</tt> output</em>: Input is specified as a <tt>String</tt> object + * and output is returned as another. In order to improve memory performance, all escape and unescape + * operations <u>will return the exact same input object as output if no escape/unescape modifications + * are required</u>.</li> + * <li><em><tt>String</tt> input, <tt>java.io.Writer</tt> output</em>: Input will be read from a String + * and output will be written into the specified <tt>java.io.Writer</tt>.</li> + * <li><em><tt>java.io.Reader</tt> input, <tt>java.io.Writer</tt> output</em>: Input will be read from a Reader + * and output will be written into the specified <tt>java.io.Writer</tt>.</li> + * <li><em><tt>char[]</tt> input, <tt>java.io.Writer</tt> output</em>: Input will be read from a char array + * (<tt>char[]</tt>) and output will be written into the specified <tt>java.io.Writer</tt>. + * Two <tt>int</tt> arguments called <tt>offset</tt> and <tt>len</tt> will be + * used for specifying the part of the <tt>char[]</tt> that should be escaped/unescaped. These methods + * should be called with <tt>offset = 0</tt> and <tt>len = text.length</tt> in order to process + * the whole <tt>char[]</tt>.</li> + * </ul> + * + * <strong><u>Glossary</u></strong> + * + * <dl> + * <dt>NCR</dt> + * <dd>Named Character Reference or <em>Character Entity Reference</em>: textual + * representation of an Unicode codepoint: <tt>&aacute;</tt></dd> + * <dt>DCR</dt> + * <dd>Decimal Character Reference: base-10 numerical representation of an Unicode codepoint: + * <tt>&#225;</tt></dd> + * <dt>HCR</dt> + * <dd>Hexadecimal Character Reference: hexadecimal numerical representation of an Unicode codepoint: + * <tt>&#xE1;</tt></dd> + * <dt>Unicode Codepoint</dt> + * <dd>Each of the <tt>int</tt> values conforming the Unicode code space. + * Normally corresponding to a Java <tt>char</tt> primitive value (codepoint <= <tt>\uFFFF</tt>), + * but might be two <tt>char</tt>s for codepoints <tt>\u10000</tt> to <tt>\u10FFFF</tt> if the + * first <tt>char</tt> is a high surrogate (<tt>\uD800</tt> to <tt>\uDBFF</tt>) and the + * second is a low surrogate (<tt>\uDC00</tt> to <tt>\uDFFF</tt>).</dd> + * </dl> + * + * <strong><u>References</u></strong> + * + * <p> + * The following references apply: + * </p> + * <ul> + * <li><a href="http://www.w3.org/International/questions/qa-escapes" target="_blank">Using character escapes in + * markup and CSS</a> [w3.org]</li> + * <li><a href="http://www.w3.org/TR/html4/sgml/entities.html" target="_blank">Named Character References (or + * <em>Character entity references</em>) in HTML 4</a> [w3.org]</li> + * <li><a href="http://www.w3.org/TR/html5/syntax.html#named-character-references" target="_blank">Named Character + * References (or <em>Character entity references</em>) in HTML5</a> [w3.org]</li> + * <li><a href="http://www.w3.org/TR/html51/syntax.html#named-character-references" target="_blank">Named Character + * References (or <em>Character entity references</em>) in HTML 5.1</a> [w3.org]</li> + * <li><a href="http://www.w3.org/TR/html5/syntax.html#consume-a-character-reference" target="_blank">How to consume a + * character reference (HTML5 specification)</a> [w3.org]</li> + * <li><a href="https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet" + * target="_blank">OWASP XSS (Cross Site Scripting) Prevention Cheat Sheet</a> [owasp.org]</li> + * <li><a href="http://www.oracle.com/technetwork/articles/javase/supplementary-142654.html" + * target="_blank">Supplementary characters in the Java Platform</a> [oracle.com]</li> + * </ul> + * + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +public final class HtmlEscape { + + + + + /** + * <p> + * Perform an HTML5 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML5 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(String, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @return The escaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no escaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String escapeHtml5(final String text) { + return escapeHtml(text, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML5 level 1 (XML-style) <strong>escape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml4Xml(String)} because + * it will escape the apostrophe as <tt>&apos;</tt>, whereas in HTML 4 such NCR does not exist + * (the decimal numeric reference <tt>&#39;</tt> is used instead). + * </p> + * <p> + * This method calls {@link #escapeHtml(String, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @return The escaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no escaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String escapeHtml5Xml(final String text) { + return escapeHtml(text, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML 4 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(String, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @return The escaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no escaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String escapeHtml4(final String text) { + return escapeHtml(text, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 1 (XML-style) <strong>escape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml5Xml(String)} because + * it will escape the apostrophe as <tt>&#39;</tt>, whereas in HTML5 there is a specific NCR for + * such character (<tt>&apos;</tt>). + * </p> + * <p> + * This method calls {@link #escapeHtml(String, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @return The escaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no escaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String escapeHtml4Xml(final String text) { + return escapeHtml(text, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform a (configurable) HTML <strong>escape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * This method will perform an escape operation according to the specified + * {@link org.unbescape.html.HtmlEscapeType} and {@link org.unbescape.html.HtmlEscapeLevel} + * argument values. + * </p> + * <p> + * All other <tt>String</tt>-based <tt>escapeHtml*(...)</tt> methods call this one with preconfigured + * <tt>type</tt> and <tt>level</tt> values. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param type the type of escape operation to be performed, see {@link org.unbescape.html.HtmlEscapeType}. + * @param level the escape level to be applied, see {@link org.unbescape.html.HtmlEscapeLevel}. + * @return The escaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no escaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String escapeHtml(final String text, final HtmlEscapeType type, final HtmlEscapeLevel level) { + + if (type == null) { + throw new IllegalArgumentException("The 'type' argument cannot be null"); + } + + if (level == null) { + throw new IllegalArgumentException("The 'level' argument cannot be null"); + } + + return HtmlEscapeUtil.escape(text, type, level); + + } + + + + + + + + /** + * <p> + * Perform an HTML5 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>String</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML5 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(String, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml5(final String text, final Writer writer) + throws IOException { + escapeHtml(text, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML5 level 1 (XML-style) <strong>escape</strong> operation on a <tt>String</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml4Xml(String, Writer)} because + * it will escape the apostrophe as <tt>&apos;</tt>, whereas in HTML 4 such NCR does not exist + * (the decimal numeric reference <tt>&#39;</tt> is used instead). + * </p> + * <p> + * This method calls {@link #escapeHtml(String, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml5Xml(final String text, final Writer writer) + throws IOException { + escapeHtml(text, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>String</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML 4 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(String, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml4(final String text, final Writer writer) + throws IOException { + escapeHtml(text, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 1 (XML-style) <strong>escape</strong> operation on a <tt>String</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml5Xml(String, Writer)} because + * it will escape the apostrophe as <tt>&#39;</tt>, whereas in HTML5 there is a specific NCR for + * such character (<tt>&apos;</tt>). + * </p> + * <p> + * This method calls {@link #escapeHtml(String, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml4Xml(final String text, final Writer writer) + throws IOException { + escapeHtml(text, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform a (configurable) HTML <strong>escape</strong> operation on a <tt>String</tt> input, writing + * results to a <tt>Writer</tt>. + * </p> + * <p> + * This method will perform an escape operation according to the specified + * {@link org.unbescape.html.HtmlEscapeType} and {@link org.unbescape.html.HtmlEscapeLevel} + * argument values. + * </p> + * <p> + * All other <tt>String</tt>/<tt>Writer</tt>-based <tt>escapeHtml*(...)</tt> methods call this one with preconfigured + * <tt>type</tt> and <tt>level</tt> values. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @param type the type of escape operation to be performed, see {@link org.unbescape.html.HtmlEscapeType}. + * @param level the escape level to be applied, see {@link org.unbescape.html.HtmlEscapeLevel}. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml(final String text, final Writer writer, final HtmlEscapeType type, final HtmlEscapeLevel level) + throws IOException { + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + + if (type == null) { + throw new IllegalArgumentException("The 'type' argument cannot be null"); + } + + if (level == null) { + throw new IllegalArgumentException("The 'level' argument cannot be null"); + } + + HtmlEscapeUtil.escape(new InternalStringReader(text), writer, type, level); + + } + + + + + + + + /** + * <p> + * Perform an HTML5 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>Reader</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML5 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(Reader, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml5(final Reader reader, final Writer writer) + throws IOException { + escapeHtml(reader, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML5 level 1 (XML-style) <strong>escape</strong> operation on a <tt>Reader</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml4Xml(Reader, Writer)} because + * it will escape the apostrophe as <tt>&apos;</tt>, whereas in HTML 4 such NCR does not exist + * (the decimal numeric reference <tt>&#39;</tt> is used instead). + * </p> + * <p> + * This method calls {@link #escapeHtml(Reader, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml5Xml(final Reader reader, final Writer writer) + throws IOException { + escapeHtml(reader, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>Reader</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML 4 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(Reader, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml4(final Reader reader, final Writer writer) + throws IOException { + escapeHtml(reader, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 1 (XML-style) <strong>escape</strong> operation on a <tt>Reader</tt> input, + * writing results to a <tt>Writer</tt>. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as {@link #escapeHtml5Xml(Reader, Writer)} because + * it will escape the apostrophe as <tt>&#39;</tt>, whereas in HTML5 there is a specific NCR for + * such character (<tt>&apos;</tt>). + * </p> + * <p> + * This method calls {@link #escapeHtml(Reader, Writer, HtmlEscapeType, HtmlEscapeLevel)} with the following + * preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml4Xml(final Reader reader, final Writer writer) + throws IOException { + escapeHtml(reader, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform a (configurable) HTML <strong>escape</strong> operation on a <tt>Reader</tt> input, writing + * results to a <tt>Writer</tt>. + * </p> + * <p> + * This method will perform an escape operation according to the specified + * {@link org.unbescape.html.HtmlEscapeType} and {@link org.unbescape.html.HtmlEscapeLevel} + * argument values. + * </p> + * <p> + * All other <tt>Reader</tt>/<tt>Writer</tt>-based <tt>escapeHtml*(...)</tt> methods call this one with preconfigured + * <tt>type</tt> and <tt>level</tt> values. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @param type the type of escape operation to be performed, see {@link org.unbescape.html.HtmlEscapeType}. + * @param level the escape level to be applied, see {@link org.unbescape.html.HtmlEscapeLevel}. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void escapeHtml(final Reader reader, final Writer writer, final HtmlEscapeType type, final HtmlEscapeLevel level) + throws IOException { + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + + if (type == null) { + throw new IllegalArgumentException("The 'type' argument cannot be null"); + } + + if (level == null) { + throw new IllegalArgumentException("The 'level' argument cannot be null"); + } + + HtmlEscapeUtil.escape(reader, writer, type, level); + + } + + + + + + + + /** + * <p> + * Perform an HTML5 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML5 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(char[], int, int, Writer, HtmlEscapeType, HtmlEscapeLevel)} + * with the following preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be escaped. + * @param offset the position in <tt>text</tt> at which the escape operation should start. + * @param len the number of characters in <tt>text</tt> that should be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + */ + public static void escapeHtml5(final char[] text, final int offset, final int len, final Writer writer) + throws IOException { + escapeHtml(text, offset, len, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML5 level 1 (XML-style) <strong>escape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as + * {@link #escapeHtml4Xml(char[], int, int, Writer)} because + * it will escape the apostrophe as <tt>&apos;</tt>, whereas in HTML 4 such NCR does not exist + * (the decimal numeric reference <tt>&#39;</tt> is used instead). + * </p> + * <p> + * This method calls {@link #escapeHtml(char[], int, int, Writer, HtmlEscapeType, HtmlEscapeLevel)} + * with the following preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be escaped. + * @param offset the position in <tt>text</tt> at which the escape operation should start. + * @param len the number of characters in <tt>text</tt> that should be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + */ + public static void escapeHtml5Xml(final char[] text, final int offset, final int len, final Writer writer) + throws IOException { + escapeHtml(text, offset, len, writer, HtmlEscapeType.HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 2 (result is ASCII) <strong>escape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * <em>Level 2</em> means this method will escape: + * </p> + * <ul> + * <li>The five markup-significant characters: <tt><</tt>, <tt>></tt>, <tt>&</tt>, + * <tt>"</tt> and <tt>'</tt></li> + * <li>All non ASCII characters.</li> + * </ul> + * <p> + * This escape will be performed by replacing those chars by the corresponding HTML 4 Named Character References + * (e.g. <tt>'&acute;'</tt>) when such NCR exists for the replaced character, and replacing by a decimal + * character reference (e.g. <tt>'&#8345;'</tt>) when there there is no NCR for the replaced character. + * </p> + * <p> + * This method calls {@link #escapeHtml(char[], int, int, Writer, HtmlEscapeType, HtmlEscapeLevel)} + * with the following preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be escaped. + * @param offset the position in <tt>text</tt> at which the escape operation should start. + * @param len the number of characters in <tt>text</tt> that should be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + */ + public static void escapeHtml4(final char[] text, final int offset, final int len, final Writer writer) + throws IOException { + escapeHtml(text, offset, len, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform an HTML 4 level 1 (XML-style) <strong>escape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * <em>Level 1</em> means this method will only escape the five markup-significant characters: + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt>. It is called + * <em>XML-style</em> in order to link it with JSP's <tt>escapeXml</tt> attribute in JSTL's + * <tt><c:out ... /></tt> tags. + * </p> + * <p> + * Note this method may <strong>not</strong> produce the same results as + * {@link #escapeHtml5Xml(char[], int, int, Writer)} because it will escape the apostrophe as + * <tt>&#39;</tt>, whereas in HTML5 there is a specific NCR for such character (<tt>&apos;</tt>). + * </p> + * <p> + * This method calls {@link #escapeHtml(char[], int, int, Writer, HtmlEscapeType, HtmlEscapeLevel)} + * with the following preconfigured values: + * </p> + * <ul> + * <li><tt>type</tt>: + * {@link org.unbescape.html.HtmlEscapeType#HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL}</li> + * <li><tt>level</tt>: + * {@link org.unbescape.html.HtmlEscapeLevel#LEVEL_1_ONLY_MARKUP_SIGNIFICANT}</li> + * </ul> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be escaped. + * @param offset the position in <tt>text</tt> at which the escape operation should start. + * @param len the number of characters in <tt>text</tt> that should be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + */ + public static void escapeHtml4Xml(final char[] text, final int offset, final int len, final Writer writer) + throws IOException { + escapeHtml(text, offset, len, writer, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL, + HtmlEscapeLevel.LEVEL_1_ONLY_MARKUP_SIGNIFICANT); + } + + + /** + * <p> + * Perform a (configurable) HTML <strong>escape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * This method will perform an escape operation according to the specified + * {@link org.unbescape.html.HtmlEscapeType} and {@link org.unbescape.html.HtmlEscapeLevel} + * argument values. + * </p> + * <p> + * All other <tt>char[]</tt>-based <tt>escapeHtml*(...)</tt> methods call this one with preconfigured + * <tt>type</tt> and <tt>level</tt> values. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be escaped. + * @param offset the position in <tt>text</tt> at which the escape operation should start. + * @param len the number of characters in <tt>text</tt> that should be escaped. + * @param writer the <tt>java.io.Writer</tt> to which the escaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @param type the type of escape operation to be performed, see {@link org.unbescape.html.HtmlEscapeType}. + * @param level the escape level to be applied, see {@link org.unbescape.html.HtmlEscapeLevel}. + * @throws IOException if an input/output exception occurs + */ + public static void escapeHtml(final char[] text, final int offset, final int len, final Writer writer, + final HtmlEscapeType type, final HtmlEscapeLevel level) + throws IOException { + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + + if (type == null) { + throw new IllegalArgumentException("The 'type' argument cannot be null"); + } + + if (level == null) { + throw new IllegalArgumentException("The 'level' argument cannot be null"); + } + + final int textLen = (text == null? 0 : text.length); + + if (offset < 0 || offset > textLen) { + throw new IllegalArgumentException( + "Invalid (offset, len). offset=" + offset + ", len=" + len + ", text.length=" + textLen); + } + + if (len < 0 || (offset + len) > textLen) { + throw new IllegalArgumentException( + "Invalid (offset, len). offset=" + offset + ", len=" + len + ", text.length=" + textLen); + } + + HtmlEscapeUtil.escape(text, offset, len, writer, type, level); + + } + + + + + + + /** + * <p> + * Perform an HTML <strong>unescape</strong> operation on a <tt>String</tt> input. + * </p> + * <p> + * No additional configuration arguments are required. Unescape operations + * will always perform <em>complete</em> unescape of NCRs (whole HTML5 set supported), decimal + * and hexadecimal references. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be unescaped. + * @return The unescaped result <tt>String</tt>. As a memory-performance improvement, will return the exact + * same object as the <tt>text</tt> input argument if no unescaping modifications were required (and + * no additional <tt>String</tt> objects will be created during processing). Will + * return <tt>null</tt> if input is <tt>null</tt>. + */ + public static String unescapeHtml(final String text) { + if (text == null) { + return null; + } + if (text.indexOf('&') < 0) { + // Fail fast, avoid more complex (and less JIT-table) method to execute if not needed + return text; + } + return HtmlEscapeUtil.unescape(text); + } + + + + /** + * <p> + * Perform an HTML <strong>unescape</strong> operation on a <tt>String</tt> input, writing results to + * a <tt>Writer</tt>. + * </p> + * <p> + * No additional configuration arguments are required. Unescape operations + * will always perform <em>complete</em> unescape of NCRs (whole HTML5 set supported), decimal + * and hexadecimal references. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>String</tt> to be unescaped. + * @param writer the <tt>java.io.Writer</tt> to which the unescaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void unescapeHtml(final String text, final Writer writer) + throws IOException { + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + if (text == null) { + return; + } + if (text.indexOf('&') < 0) { + // Fail fast, avoid more complex (and less JIT-table) method to execute if not needed + writer.write(text); + return; + } + + HtmlEscapeUtil.unescape(new InternalStringReader(text), writer); + + } + + + + /** + * <p> + * Perform an HTML <strong>unescape</strong> operation on a <tt>Reader</tt> input, writing results to + * a <tt>Writer</tt>. + * </p> + * <p> + * No additional configuration arguments are required. Unescape operations + * will always perform <em>complete</em> unescape of NCRs (whole HTML5 set supported), decimal + * and hexadecimal references. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param reader the <tt>Reader</tt> reading the text to be unescaped. + * @param writer the <tt>java.io.Writer</tt> to which the unescaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + * + * @since 1.1.2 + */ + public static void unescapeHtml(final Reader reader, final Writer writer) + throws IOException { + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + + HtmlEscapeUtil.unescape(reader, writer); + + } + + + + /** + * <p> + * Perform an HTML <strong>unescape</strong> operation on a <tt>char[]</tt> input. + * </p> + * <p> + * No additional configuration arguments are required. Unescape operations + * will always perform <em>complete</em> unescape of NCRs (whole HTML5 set supported), decimal + * and hexadecimal references. + * </p> + * <p> + * This method is <strong>thread-safe</strong>. + * </p> + * + * @param text the <tt>char[]</tt> to be unescaped. + * @param offset the position in <tt>text</tt> at which the unescape operation should start. + * @param len the number of characters in <tt>text</tt> that should be unescaped. + * @param writer the <tt>java.io.Writer</tt> to which the unescaped result will be written. Nothing will + * be written at all to this writer if input is <tt>null</tt>. + * @throws IOException if an input/output exception occurs + */ + public static void unescapeHtml(final char[] text, final int offset, final int len, final Writer writer) + throws IOException{ + + if (writer == null) { + throw new IllegalArgumentException("Argument 'writer' cannot be null"); + } + + final int textLen = (text == null? 0 : text.length); + + if (offset < 0 || offset > textLen) { + throw new IllegalArgumentException( + "Invalid (offset, len). offset=" + offset + ", len=" + len + ", text.length=" + textLen); + } + + if (len < 0 || (offset + len) > textLen) { + throw new IllegalArgumentException( + "Invalid (offset, len). offset=" + offset + ", len=" + len + ", text.length=" + textLen); + } + + HtmlEscapeUtil.unescape(text, offset, len, writer); + + } + + + + + private HtmlEscape() { + super(); + } + + + + /* + * This is basically a very simplified, thread-unsafe version of StringReader that should + * perform better than the original StringReader by removing all synchronization structures. + * + * Note the only implemented methods are those that we know are really used from within the + * stream-based escape/unescape operations. + */ + private static final class InternalStringReader extends Reader { + + private String str; + private int length; + private int next = 0; + + public InternalStringReader(final String s) { + super(); + this.str = s; + this.length = s.length(); + } + + @Override + public int read() throws IOException { + if (this.next >= length) { + return -1; + } + return this.str.charAt(this.next++); + } + + @Override + public int read(final char[] cbuf, final int off, final int len) throws IOException { + if ((off < 0) || (off > cbuf.length) || (len < 0) || + ((off + len) > cbuf.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + if (this.next >= this.length) { + return -1; + } + int n = Math.min(this.length - this.next, len); + this.str.getChars(this.next, this.next + n, cbuf, off); + this.next += n; + return n; + } + + @Override + public void close() throws IOException { + this.str = null; // Just set the reference to null, help the GC + } + + } + + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeLevel.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeLevel.java new file mode 100755 index 0000000..33a70e3 --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeLevel.java @@ -0,0 +1,138 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +/** + * <p> + * Levels defined for HTML escape/unescape operations: + * </p> + * + * <ul> + * <li><strong>Level 0</strong>: Escape only markup-significant characters, excluding the apostrophe. Therefore + * <tt><</tt>, <tt>></tt>, <tt>&</tt> and <tt>"</tt> will be escaped. This level can be + * used for escaping texts and also tag attributes that are always surrounded by double quotes, whenever the + * apostrophe (<tt>'</tt>) is considered a <em>safe</em> character and the user prefers it not to be + * escaped for legibility reasons (e.g. might denote literals in expression languages like OGNL). + * Note the result of a level-0 escape operation might still contain non-ASCII characters if they existed + * in input, and therefore you will still need to correctly manage your input/output character + * encoding settings.</li> + * <li><strong>Level 1</strong>: Escape only markup-significant characters (including the apostrophe). Therefore + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt> will be escaped. + * This level is sometimes called <em>XML escape</em> or <strong><em>XML-style escape</em></strong>, though it + * is not exactly equivalent to XML due to some HTML specificities. It is equivalent to the JSP escape + * configured by the <tt>escapeXml</tt> attribute in JSTL's <tt><c:out ... /></tt> tags, and safe + * for use in texts and also tag attributes that are always quoted —be it with single (apostrophe) or + * double quotes. Note the result of a level-1 escape operation might still contain non-ASCII characters if they + * existed in input, and therefore you will still need to correctly manage your input/output character + * encoding settings.</li> + * <li><strong>Level 2</strong>: Escape all markup-significant characters (as defined in level 1), plus all + * non-ASCII characters. The result of a level-2 escape operation is therefore always ASCII-only text, and + * safer to use in complex scenarios with mixed input/output character encodings. This level can be used + * for escaping texts and also tag attributes that are always quoted —be it with single (apostrophe) or + * double quotes.</li> + * <li><strong>Level 3</strong>: Escape all non-alphanumeric characters, this is, all but those in the + * <tt>A</tt>-<tt>Z</tt>, <tt>a</tt>-<tt>z</tt> and <tt>0</tt>-<tt>9</tt> ranges. This level + * can be safely used for escaping texts and also tag attributes, even when these tag attributes are + * unquoted.</li> + * <li><strong>Level 4</strong>: Escape all characters, even alphanumeric ones.</li> + * </ul> + * + * <p> + * For further information, see the <em>Glossary</em> and the <em>References</em> sections at the + * documentation for the {@link org.unbescape.html.HtmlEscape} class. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +public enum HtmlEscapeLevel { + + /** + * Level 0 escape: escape only markup-significant characters, excluding the apostrophe: + * <tt><</tt>, <tt>></tt>, <tt>&</tt> and <tt>"</tt> + */ + LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS(0), + + /** + * Level 1 escape (<em>XML-style</em>): escape only markup-significant characters (including the apostrophe): + * <tt><</tt>, <tt>></tt>, <tt>&</tt>, <tt>"</tt> and <tt>'</tt> + */ + LEVEL_1_ONLY_MARKUP_SIGNIFICANT(1), + + /** + * Level 2 escape: escape markup-significant characters plus all non-ASCII characters (result will always be ASCII). + */ + LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT(2), + + /** + * Level 3 escape: escape all non-alphanumeric characteres (escape all but those in the + * <tt>A</tt>-<tt>Z</tt>, <tt>a</tt>-<tt>z</tt> and <tt>0</tt>-<tt>9</tt> ranges). + */ + LEVEL_3_ALL_NON_ALPHANUMERIC(3), + + /** + * Level 4 escape: escape all characters, including alphanumeric. + */ + LEVEL_4_ALL_CHARACTERS(4); + + + + + private final int escapeLevel; + + + /** + * <p> + * Utility method for obtaining an enum value from its corresponding <tt>int</tt> level value. + * </p> + * + * @param level the level + * @return the escape level enum constant, or <tt>IllegalArgumentException</tt> if level does not exist. + */ + public static HtmlEscapeLevel forLevel(final int level) { + switch (level) { + case 0: return LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS; + case 1: return LEVEL_1_ONLY_MARKUP_SIGNIFICANT; + case 2: return LEVEL_2_ALL_NON_ASCII_PLUS_MARKUP_SIGNIFICANT; + case 3: return LEVEL_3_ALL_NON_ALPHANUMERIC; + case 4: return LEVEL_4_ALL_CHARACTERS; + default: + throw new IllegalArgumentException("No escape level enum constant defined for level: " + level); + } + } + + + HtmlEscapeLevel(final int escapeLevel) { + this.escapeLevel = escapeLevel; + } + + /** + * Return the <tt>int</tt> escape level. + * + * @return the escape level. + */ + public int getEscapeLevel() { + return this.escapeLevel; + } + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeSymbols.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeSymbols.java new file mode 100755 index 0000000..9152bef --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeSymbols.java @@ -0,0 +1,573 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * <p> + * Instances of this class group all the complex data structures needed to support full escape and unescape + * operations for HTML. + * </p> + * <p> + * Most of the fields in objects of this class are package-accessible, as the class itself is, in order to allow + * them (the fields) to be directly accessed from the classes doing the real escape/unescape (basically, + * the {@link HtmlEscapeUtil} class. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +final class HtmlEscapeSymbols { + + + /* + * GLOSSARY + * ------------------------ + * + * NCR + * Named Character Reference or Character Entity Reference: textual + * representation of an Unicode codepoint: á + * + * DCR + * Decimal Character Reference: base-10 numerical representation of an Unicode codepoint: á + * + * HCR + * Hexadecimal Character Reference: hexadecimal numerical representation of an Unicode codepoint: á + * + * Unicode Codepoint + * Each of the int values conforming the Unicode code space. + * Normally corresponding to a Java char primitive value (codepoint <= \uFFFF), + * but might be two chars for codepoints \u10000 to \u10FFFF if the first char is a high + * surrogate (\uD800 to \uDBFF) and the second is a low surrogate (\uDC00 to \uDFFF). + * See: http://www.oracle.com/technetwork/articles/javase/supplementary-142654.html + * + */ + + + + + /* + * Length of the array used for holding the 'base' NCRS indexed by the codepoints themselves. This size + * (0x2fff - 12287) is considered enough to hold most of the NCRS that should be needed (HTML4 has 252 + * NCRs with a maximum codepoint of 0x2666 - HTML5 has 2125 NCRs with a maximum codepoint of 120171, but + * only 138 scarcely used NCRs live above codepoint 0x2fff so an overflow map should be enough for + * those 138 cases). + */ + static final int NCRS_BY_CODEPOINT_LEN = 0x2fff; + + /* + * This array will contain the NCRs for the first NCRS_BY_CODEPOINT_LEN (0x2fff) codepoints, indexed by + * the codepoints themselves so that they (even in the form of mere char's) can be used for array random access. + * - Values are short in order to index values at the SORTED_NCRS array. This avoids the need for this + * array to hold String pointers, which would be 4 bytes in size each (compared to shorts, which are 2 bytes). + * - Chars themselves or int codepoints can (will, in fact) be used as indexes. + * - Given values are short, the maximum amount of total references this class can handle is 0x7fff = 32767 + * (which is safe, because HTML5 has 2125). + * - All XML and HTML4 NCRs will fit in this array. In the case of HTML5 NCRs, only 138 of the 2125 will + * not fit here (NCRs assigned to codepoints > 0x2fff), and an overflow map will be provided for them. + * - Approximate size will be 16 (header) + 12287 * 2 = 24590 bytes. + */ + final short[] NCRS_BY_CODEPOINT = new short[NCRS_BY_CODEPOINT_LEN]; + + /* + * This map will work as an overflow of the NCRS_BY_CODEPOINT array, so that the codepoint-to-NCR relation is + * stored here (with hash-based access) for codepoints >= NCRS_BY_CODEPOINT_LEN (0x2fff). + * - The use of a Map here still allows for reasonabily fast access for those rare cases in which codepoints above + * 0x2fff are used. + * - In the real world, this map will contain the 138 values needed by HTML5 for codepoints >= 0x2fff. + * - Approximate max size will be (being a complex object like a Map, it's a rough approximation): + * 16 (header) + 138 * (16 (entry header) + 16*2 (key, value headers) + 4 (key) + 2 (value)) = 7468 bytes + */ + final Map<Integer,Short> NCRS_BY_CODEPOINT_OVERFLOW;// No need to instantiate it until we know it's needed + + /* + * Maximum char value inside the ASCII plane + */ + static final char MAX_ASCII_CHAR = 0x7f; + + /* + * This array will hold the 'escape level' assigned to each ASCII character (codepoint), 0x0 to 0x7f and also + * a level for the rest of non-ASCII characters. + * - These levels are used to configure how (and if) escape operations should ignore ASCII or non-ASCII + * characters, or escape them somehow if required. + * - Each HtmlEscapeSymbols structure will define a different set of levels for ASCII chars, according to their needs. + * - Position 0x7f + 1 represents all the non-ASCII characters. The specified value will determine whether + * all non-ASCII characters have to be escaped or not. + */ + final byte[] ESCAPE_LEVELS = new byte[MAX_ASCII_CHAR + 2]; + + /* + * This array will contain all the NCRs, alphabetically ordered. + * - Positions in this array will correspond to positions in the SORTED_CODEPOINTS array, so that one array + * (this one) holds the NCRs while the other one holds the codepoint(s) such NCRs refer to. + * - Gives the opportunity to store all NCRs in alphabetical order and therefore be able to perform + * binary search operations in order to quickly find NCRs (and translate to codepoints) when unescaping. + * - Note this array will contain: + * * All NCRs referenced from NCRS_BY_CODEPOINT + * * NCRs whose codepoint is >= 0x2fff and therefore live in NCRS_BY_CODEPOINT_OVERFLOW + * * NCRs which are not referenced in any of the above because they are a shortcut for (and completely + * equivalent to) a sequence of two codepoints. These NCRs will only be unescaped, but never escaped. + * - Max size in real world, when populated for HTML5: 2125 NCRs * 4 bytes/objref -> 8500 bytes, plus the texts. + */ + final char[][] SORTED_NCRS; + + /* + * This array contains all the codepoints corresponding to the NCRs stored in SORTED_NCRS. This array is ordered + * so that each index in SORTED_NCRS can also be used to retrieve the corresponding CODEPOINT when used on this array. + * - Values in this array can be positive (= single codepoint) or negative (= double codepoint, will need further + * resolution by means of the DOUBLE_CODEPOINTS array) + * - Max size in real world, when populated for HTML5: 2125 NCRs * 4 bytes/objref -> 8500 bytes. + */ + final int[] SORTED_CODEPOINTS; + + + /* + * This array stores the sequences of two codepoints that are escaped as a single NCR. The indexes of this array are + * referenced as negative numbers at the SORTED_CODEPOINTS array, and the values are int[2], containing the + * sequence of codepoints. HTML4 has no NCRs like this, HTML5 has 93. + * - Note this array is only used in UNESCAPE operations. Double-codepoint NCR escape is not performed because + * the resulting characters are exactly equivalent to the escape of the two codepoints separately. + * - Max size in real world, when populated for HTML5 (rough approximate): 93 * (4 (ref) + 16 + 2 * 4) = 2604 bytes + */ + final int[][] DOUBLE_CODEPOINTS; + + + /* + * This constant will be used at the NCRS_BY_CODEPOINT array to specify there is no NCR associated with a + * codepoint. + */ + static final short NO_NCR = (short) 0; + + + + + /* + * Constants holding the definition of all the HtmlEscapeSymbols for HTML4 and HTML5, to be used in escape and + * unescape operations. + */ + static final HtmlEscapeSymbols HTML4_SYMBOLS; + static final HtmlEscapeSymbols HTML5_SYMBOLS; + + + + + static { + + HTML4_SYMBOLS = Html4EscapeSymbolsInitializer.initializeHtml4(); + HTML5_SYMBOLS = Html5EscapeSymbolsInitializer.initializeHtml5(); + + } + + + + + + /* + * Create a new HtmlEscapeSymbols structure. This will initialize all the structures needed to cover the + * specified references and escape levels, including sorted arrays, overflow maps, etc. + */ + HtmlEscapeSymbols(final References references, final byte[] escapeLevels) { + + super(); + + // Initialize ASCII escape levels: just copy the array + System.arraycopy(escapeLevels, 0, ESCAPE_LEVELS, 0, (0x7f + 2)); + + + // Initialize some auxiliary structures + final List<char[]> ncrs = new ArrayList<char[]>(references.references.size() + 5); + final List<Integer> codepoints = new ArrayList<Integer>(references.references.size() + 5); + final List<int[]> doubleCodepoints = new ArrayList<int[]>(100); + final Map<Integer,Short> ncrsByCodepointOverflow = new HashMap<Integer, Short>(20); + + // For each reference, initialize its corresponding codepoint -> ncr and ncr -> codepoint structures + for (final Reference reference : references.references) { + + final char[] referenceNcr = reference.ncr; + final int[] referenceCodepoints = reference.codepoints; + + ncrs.add(referenceNcr); + + if (referenceCodepoints.length == 1) { + // Only one codepoint (might be > 1 chars, though), this is the normal case + + final int referenceCodepoint = referenceCodepoints[0]; + codepoints.add(Integer.valueOf(referenceCodepoint)); + + } else if (referenceCodepoints.length == 2) { + // Two codepoints, therefore this NCR will translate when unescaping into a two-codepoint + // (probably two-char, too) sequence. We will use a negative codepoint value to signal this. + + doubleCodepoints.add(referenceCodepoints); + // Will need to subtract one from its index when unescaping (codepoint = -1 -> position 0) + codepoints.add(Integer.valueOf((-1) * doubleCodepoints.size())); + + } else { + + throw new RuntimeException( + "Unsupported codepoints #: " + referenceCodepoints.length + " for " + new String(referenceNcr)); + + } + + } + + // We hadn't touched this array before. First thing to do is initialize it, as it will have a huge + // amount of "empty" (i.e. non-assigned) values. + Arrays.fill(NCRS_BY_CODEPOINT, NO_NCR); + + + // We can initialize now these arrays that will hold the NCR-to-codepoint correspondence, but we cannot copy + // them directly from our auxiliary structures because we need to order the NCRs alphabetically first. + + SORTED_NCRS = new char[ncrs.size()][]; + SORTED_CODEPOINTS = new int[codepoints.size()]; + + final List<char[]> ncrsOrdered = new ArrayList<char[]>(ncrs); + Collections.sort(ncrsOrdered, new Comparator<char[]>() { + public int compare(final char[] o1, final char[] o2) { + return HtmlEscapeSymbols.compare(o1, o2, 0, o2.length); + } + }); + + for (short i = 0; i < SORTED_NCRS.length; i++) { + + final char[] ncr = ncrsOrdered.get(i); + SORTED_NCRS[i] = ncr; + + for (short j = 0; j < SORTED_NCRS.length; j++) { + + if (Arrays.equals(ncr,ncrs.get(j))) { + + final int cp = codepoints.get(j); + SORTED_CODEPOINTS[i] = cp; + + if (cp > 0) { + // Not negative (i.e. not double-codepoint) + if (cp < NCRS_BY_CODEPOINT_LEN) { + // Not overflown + if (NCRS_BY_CODEPOINT[cp] == NO_NCR) { + // Only the first NCR for each codepoint will be used for escaping. + NCRS_BY_CODEPOINT[cp] = i; + } else { + final int positionOfCurrent = positionInList(ncrs, SORTED_NCRS[NCRS_BY_CODEPOINT[cp]]); + final int positionOfNew = positionInList(ncrs, ncr); + if (positionOfNew < positionOfCurrent) { + // The order in which NCRs were originally specified in the references argument + // marks which NCR should be used for escaping (the first one), if several NCRs + // have the same codepoint. + NCRS_BY_CODEPOINT[cp] = i; + } + } + } else { + // Codepoint should be overflown + ncrsByCodepointOverflow.put(Integer.valueOf(cp), Short.valueOf(i)); + } + } + + break; + + } + + } + + } + + + // Only create the overflow map if it is really needed. + if (ncrsByCodepointOverflow.size() > 0) { + NCRS_BY_CODEPOINT_OVERFLOW = ncrsByCodepointOverflow; + } else { + NCRS_BY_CODEPOINT_OVERFLOW = null; + } + + + // Finally, the double-codepoints structure can be initialized, if really needed. + if (doubleCodepoints.size() > 0) { + DOUBLE_CODEPOINTS = new int[doubleCodepoints.size()][]; + for (int i = 0; i < DOUBLE_CODEPOINTS.length; i++) { + DOUBLE_CODEPOINTS[i] = doubleCodepoints.get(i); + } + } else { + DOUBLE_CODEPOINTS = null; + } + + } + + + /* + * Utility method, used for determining which of the different NCRs for the same + * codepoint (when there are many) was specified first, because that is the one + * we should be using for escaping. + * (Note all of the NCRs will be available for unescaping, obviously) + */ + private static int positionInList(final List<char[]> list, final char[] element) { + int i = 0; + for (final char[] e : list) { + if (Arrays.equals(e, element)) { + return i; + } + i++; + } + return -1; + } + + + + + /* + * These two methods (two versions: for String and for char[]) compare each of the candidate + * text fragments with an NCR coming from the SORTED_NCRs array, during binary search operations. + * + * Note these methods not only perform a normal comparison (returning -1, 0 or 1), but will also + * return a negative number < -10 when a partial match is possible, this is, when the specified text + * fragment contains a complete NCR at its first chars but contains more chars afterwards. This is + * useful for matching HTML5 NCRs which do not end in ; (like 'á'), which will come in bigger fragments + * because the unescape method will have no way of differentiating the chars after the NCR from chars that + * could be in fact part of the NCR. Also note that, in the case of a partial match, (-1) * (returnValue + 10) + * will specify the number of matched chars. + * + * Note we will willingly alter order so that ';' goes always first (even before no-char). This will allow + * proper functioning of the partial-matching mechanism for NCRs that can appear both with and without + * a ';' suffix. + */ + + private static int compare(final char[] ncr, final String text, final int start, final int end) { + final int textLen = end - start; + final int maxCommon = Math.min(ncr.length, textLen); + int i; + // char 0 is discarded, will be & in both cases + for (i = 1; i < maxCommon; i++) { + final char tc = text.charAt(start + i); + if (ncr[i] < tc) { + if (tc == ';') { + return 1; + } + return -1; + } else if (ncr[i] > tc) { + if (ncr[i] == ';') { + return -1; + } + return 1; + } + } + if (ncr.length > i) { + if (ncr[i] == ';') { + return -1; + } + return 1; + } + if (textLen > i) { + if (text.charAt(start + i) == ';') { + return 1; + } + // We have a partial match. Can be an NCR not finishing in a semicolon + return - ((textLen - i) + 10); + } + return 0; + } + + private static int compare(final char[] ncr, final char[] text, final int start, final int end) { + final int textLen = end - start; + final int maxCommon = Math.min(ncr.length, textLen); + int i; + // char 0 is discarded, will be & in both cases + for (i = 1; i < maxCommon; i++) { + final char tc = text[start + i]; + if (ncr[i] < tc) { + if (tc == ';') { + return 1; + } + return -1; + } else if (ncr[i] > tc) { + if (ncr[i] == ';') { + return -1; + } + return 1; + } + } + if (ncr.length > i) { + if (ncr[i] == ';') { + return -1; + } + return 1; + } + if (textLen > i) { + if (text[start + i] == ';') { + return 1; + } + // We have a partial match. Can be an NCR not finishing in a semicolon + return - ((textLen - i) + 10); + } + return 0; + } + + + + /* + * These two methods (two versions: for String and for char[]) are used during unescape at the + * {@link HtmlEscapeUtil} class in order to quickly find the NCR corresponding to a preselected fragment + * of text (if there is such NCR). + * + * Note this operation supports partial matching (based on the above 'compare(...)' methods). That way, + * if an exact match is not found but a partial match exists, the partial match will be returned. + */ + + static int binarySearch(final char[][] values, + final String text, final int start, final int end) { + + int low = 0; + int high = values.length - 1; + + int partialIndex = Integer.MIN_VALUE; + int partialValue = Integer.MIN_VALUE; + + while (low <= high) { + + final int mid = (low + high) >>> 1; + final char[] midVal = values[mid]; + + final int cmp = compare(midVal, text, start, end); + + if (cmp == -1) { + low = mid + 1; + } else if (cmp == 1) { + high = mid - 1; + } else if (cmp < -10) { + // Partial match + low = mid + 1; + if (partialIndex == Integer.MIN_VALUE || partialValue < cmp) { + partialIndex = mid; + partialValue = cmp; // partial will always be negative, and -10. We look for the smallest partial + } + } else { + // Found!! + return mid; + } + + } + + if (partialIndex != Integer.MIN_VALUE) { + // We have a partial result. We return the closest result index as negative + (-10) + return (-1) * (partialIndex + 10); + } + + return Integer.MIN_VALUE; // Not found! + + } + + static int binarySearch(final char[][] values, + final char[] text, final int start, final int end) { + + int low = 0; + int high = values.length - 1; + + int partialIndex = Integer.MIN_VALUE; + int partialValue = Integer.MIN_VALUE; + + while (low <= high) { + + final int mid = (low + high) >>> 1; + final char[] midVal = values[mid]; + + final int cmp = compare(midVal, text, start, end); + + if (cmp == -1) { + low = mid + 1; + } else if (cmp == 1) { + high = mid - 1; + } else if (cmp < -10) { + // Partial match + low = mid + 1; + if (partialIndex == Integer.MIN_VALUE || partialValue < cmp) { + partialIndex = mid; + partialValue = cmp; // partial will always be negative, and -10. We look for the smallest partial + } + } else { + // Found!! + return mid; + } + + } + + if (partialIndex != Integer.MIN_VALUE) { + // We have a partial result. We return the closest result index as negative + (-10) + return (-1) * (partialIndex + 10); + } + + return Integer.MIN_VALUE; // Not found! + + } + + + + + + + /* + * Inner utility classes that model the named character references to be included in an initialized + * instance of the HtmlEscapeSymbols class. + */ + + + static final class References { + + private final List<Reference> references = new ArrayList<Reference>(200); + + References() { + super(); + } + + void addReference(final int codepoint, final String ncr) { + this.references.add(new Reference(ncr, new int[]{codepoint})); + } + + void addReference(final int codepoint0, final int codepoint1, final String ncr) { + this.references.add(new Reference(ncr, new int[] { codepoint0, codepoint1 })); + } + + } + + + private static final class Reference { + + private final char[] ncr; + private final int[] codepoints; + + private Reference(final String ncr, final int[] codepoints) { + super(); + this.ncr = ncr.toCharArray(); + this.codepoints = codepoints; + } + + } + + + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeType.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeType.java new file mode 100755 index 0000000..fe2affb --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeType.java @@ -0,0 +1,119 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +/** + * <p> + * Types of escape operations to be performed on HTML text: + * </p> + * + * <ul> + * <li><tt><strong>HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL</strong></tt>: Replace escaped characters + * with HTML 4 <em>Named Character References</em> (<em>Character Entity References</em>) whenever + * possible (depending on the specified {@link org.unbescape.html.HtmlEscapeLevel}), and default to + * using <em>Decimal Character References</em> for escaped characters that do not have an associated + * NCR.</li> + * <li><tt><strong>HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA</strong></tt>: Replace escaped characters + * with HTML 4 <em>Named Character References</em> (<em>Character Entity References</em>) whenever + * possible (depending on the specified {@link org.unbescape.html.HtmlEscapeLevel}), and default to + * using <em>Hexadecimal Character References</em> for escaped characters that do not have an associated + * NCR.</li> + * <li><tt><strong>HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL</strong></tt>: Replace escaped characters + * with HTML5 <em>Named Character References</em> whenever + * possible (depending on the specified {@link org.unbescape.html.HtmlEscapeLevel}), and default to + * using <em>Decimal Character References</em> for escaped characters that do not have an associated + * NCR.</li> + * <li><tt><strong>HTML5_NAMED_REFERENCES_DEFAULT_TO_HEXA</strong></tt>: Replace escaped characters + * with HTML5 <em>Named Character References</em> whenever + * possible (depending on the specified {@link org.unbescape.html.HtmlEscapeLevel}), and default to + * using <em>Hexadecimal Character References</em> for escaped characters that do not have an associated + * NCR.</li> + * <li><tt><strong>DECIMAL_REFERENCES</strong></tt>: Replace escaped characters with + * <em>Decimal Character References</em> (will never use NCRs).</li> + * <li><tt><strong>HEXADECIMAL_REFERENCES</strong></tt>: Replace escaped characters with + * <em>Hexadecimal Character References</em> (will never use NCRs).</li> + * </ul> + * + * <p> + * For further information, see the <em>Glossary</em> and the <em>References</em> sections at the + * documentation for the {@link org.unbescape.html.HtmlEscape} class. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +public enum HtmlEscapeType { + + /** + * Use HTML 4 NCRs if possible, default to Decimal Character References. + */ + HTML4_NAMED_REFERENCES_DEFAULT_TO_DECIMAL(true, false, false), + + /** + * Use HTML 4 NCRs if possible, default to Hexadecimal Character References. + */ + HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA(true, true, false), + + /** + * Use HTML5 NCRs if possible, default to Decimal Character References. + */ + HTML5_NAMED_REFERENCES_DEFAULT_TO_DECIMAL(true, false, true), + + /** + * Use HTML5 NCRs if possible, default to Hexadecimal Character References. + */ + HTML5_NAMED_REFERENCES_DEFAULT_TO_HEXA(true, true, true), + + /** + * Always use Decimal Character References (no NCRs will be used). + */ + DECIMAL_REFERENCES(false, false, false), + + /** + * Always use Hexadecimal Character References (no NCRs will be used). + */ + HEXADECIMAL_REFERENCES(false, true, false); + + + private final boolean useNCRs; + private final boolean useHexa; + private final boolean useHtml5; + + HtmlEscapeType(final boolean useNCRs, final boolean useHexa, final boolean useHtml5) { + this.useNCRs = useNCRs; + this.useHexa = useHexa; + this.useHtml5 = useHtml5; + } + + boolean getUseNCRs() { + return this.useNCRs; + } + + boolean getUseHexa() { + return this.useHexa; + } + + boolean getUseHtml5() { + return this.useHtml5; + } +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeUtil.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeUtil.java new file mode 100755 index 0000000..1c3caf4 --- /dev/null +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/html_escape/HtmlEscapeUtil.java @@ -0,0 +1,1368 @@ +/* + * ============================================================================= + * + * Copyright (c) 2014-2017, The UNBESCAPE team (http://www.unbescape.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package ohi.andre.consolelauncher.tuils.html_escape; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; + +/** + * <p> + * Internal class in charge of performing the real escape/unescape operations. + * </p> + * + * @author Daniel Fernández + * + * @since 1.0.0 + * + */ +final class HtmlEscapeUtil { + + + + /* + * GLOSSARY + * ------------------------ + * + * NCR + * Named Character Reference or Character Entity Reference: textual + * representation of an Unicode codepoint: á + * + * DCR + * Decimal Character Reference: base-10 numerical representation of an Unicode codepoint: á + * + * HCR + * Hexadecimal Character Reference: hexadecimal numerical representation of an Unicode codepoint: á + * + * Unicode Codepoint + * Each of the int values conforming the Unicode code space. + * Normally corresponding to a Java char primitive value (codepoint <= \uFFFF), + * but might be two chars for codepoints \u10000 to \u10FFFF if the first char is a high + * surrogate (\uD800 to \uDBFF) and the second is a low surrogate (\uDC00 to \uDFFF). + * See: http://www.oracle.com/technetwork/articles/javase/supplementary-142654.html + * + */ + + + + + /* + * Prefixes and suffix defined for use in decimal/hexa escape and unescape. + */ + private static final char REFERENCE_PREFIX = '&'; + private static final char REFERENCE_NUMERIC_PREFIX2 = '#'; + private static final char REFERENCE_HEXA_PREFIX3_UPPER = 'X'; + private static final char REFERENCE_HEXA_PREFIX3_LOWER = 'x'; + private static final char[] REFERENCE_DECIMAL_PREFIX = "&#".toCharArray(); + private static final char[] REFERENCE_HEXA_PREFIX = "&#x".toCharArray(); + private static final char REFERENCE_SUFFIX = ';'; + + /* + * Small utility char arrays for hexadecimal conversion + */ + private static char[] HEXA_CHARS_UPPER = "0123456789ABCDEF".toCharArray(); + private static char[] HEXA_CHARS_LOWER = "0123456789abcdef".toCharArray(); + + + + + private HtmlEscapeUtil() { + super(); + } + + + + + + /* + * Perform an escape operation, based on String, according to the specified level and type. + */ + static String escape(final String text, final HtmlEscapeType escapeType, final HtmlEscapeLevel escapeLevel) { + + if (text == null) { + return null; + } + + final int level = escapeLevel.getEscapeLevel(); + final boolean useHtml5 = escapeType.getUseHtml5(); + final boolean useNCRs = escapeType.getUseNCRs(); + final boolean useHexa = escapeType.getUseHexa(); + + final HtmlEscapeSymbols symbols = + (useHtml5? HtmlEscapeSymbols.HTML5_SYMBOLS : HtmlEscapeSymbols.HTML4_SYMBOLS); + + StringBuilder strBuilder = null; + + final int offset = 0; + final int max = text.length(); + + int readOffset = offset; + + for (int i = offset; i < max; i++) { + + final char c = text.charAt(i); + + + /* + * Shortcut: most characters will be ASCII/Alphanumeric, and we won't need to do anything at + * all for them + */ + if (c <= HtmlEscapeSymbols.MAX_ASCII_CHAR && level < symbols.ESCAPE_LEVELS[c]) { + continue; + } + + + /* + * Shortcut: we might not want to escape non-ASCII chars at all either. + */ + if (c > HtmlEscapeSymbols.MAX_ASCII_CHAR + && level < symbols.ESCAPE_LEVELS[HtmlEscapeSymbols.MAX_ASCII_CHAR + 1]) { + continue; + } + + + /* + * Compute the codepoint. This will be used instead of the char for the rest of the process. + */ + final int codepoint = Character.codePointAt(text, i); + + + /* + * At this point we know for sure we will need some kind of escape, so we + * can increase the offset and initialize the string builder if needed, along with + * copying to it all the contents pending up to this point. + */ + + if (strBuilder == null) { + strBuilder = new StringBuilder(max + 20); + } + + if (i - readOffset > 0) { + strBuilder.append(text, readOffset, i); + } + + if (Character.charCount(codepoint) > 1) { + // This is to compensate that we are actually escaping two char[] positions with a single codepoint. + i++; + } + + readOffset = i + 1; + + + /* + * ----------------------------------------------------------------------------------------- + * + * Perform the real escape, attending the different combinations of NCR, DCR and HCR needs. + * + * ----------------------------------------------------------------------------------------- + */ + + if (useNCRs) { + // We will try to use an NCR + + if (codepoint < symbols.NCRS_BY_CODEPOINT_LEN) { + // codepoint < 0x2fff - all HTML4, most HTML5 + + final short ncrIndex = symbols.NCRS_BY_CODEPOINT[codepoint]; + if (ncrIndex != symbols.NO_NCR) { + // There is an NCR for this codepoint! + strBuilder.append(symbols.SORTED_NCRS[ncrIndex]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } else if (symbols.NCRS_BY_CODEPOINT_OVERFLOW != null) { + // codepoint >= 0x2fff. NCR, if exists, will live at the overflow map (if there is one). + + final Short ncrIndex = symbols.NCRS_BY_CODEPOINT_OVERFLOW.get(Integer.valueOf(codepoint)); + if (ncrIndex != null) { + strBuilder.append(symbols.SORTED_NCRS[ncrIndex.shortValue()]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } + + } + + /* + * No NCR-escape was possible (or allowed), so we need decimal/hexa escape. + */ + + if (useHexa) { + strBuilder.append(REFERENCE_HEXA_PREFIX); + strBuilder.append(Integer.toHexString(codepoint)); + } else { + strBuilder.append(REFERENCE_DECIMAL_PREFIX); + strBuilder.append(String.valueOf(codepoint)); + } + strBuilder.append(REFERENCE_SUFFIX); + + } + + + /* + * ----------------------------------------------------------------------------------------------- + * Final cleaning: return the original String object if no escape was actually needed. Otherwise + * append the remaining unescaped text to the string builder and return. + * ----------------------------------------------------------------------------------------------- + */ + + if (strBuilder == null) { + return text; + } + + if (max - readOffset > 0) { + strBuilder.append(text, readOffset, max); + } + + return strBuilder.toString(); + + } + + + + + + /* + * Perform an escape operation, based on a Reader, according to the specified level and type and writing the + * result to a Writer. + * + * Note this reader is going to be read char-by-char, so some kind of buffering might be appropriate if this + * is an inconvenience for the specific Reader implementation. + */ + static void escape( + final Reader reader, final Writer writer, final HtmlEscapeType escapeType, final HtmlEscapeLevel escapeLevel) + throws IOException { + + if (reader == null) { + return; + } + + final int level = escapeLevel.getEscapeLevel(); + final boolean useHtml5 = escapeType.getUseHtml5(); + final boolean useNCRs = escapeType.getUseNCRs(); + final boolean useHexa = escapeType.getUseHexa(); + + final HtmlEscapeSymbols symbols = + (useHtml5? HtmlEscapeSymbols.HTML5_SYMBOLS : HtmlEscapeSymbols.HTML4_SYMBOLS); + + int c1, c2; // c1: current char, c2: next char + + c2 = reader.read(); + + while (c2 >= 0) { + + c1 = c2; + c2 = reader.read(); + + + /* + * Shortcut: most characters will be ASCII/Alphanumeric, and we won't need to do anything at + * all for them + */ + if (c1 <= HtmlEscapeSymbols.MAX_ASCII_CHAR && level < symbols.ESCAPE_LEVELS[c1]) { + writer.write(c1); + continue; + } + + + /* + * Shortcut: we might not want to escape non-ASCII chars at all either. + */ + if (c1 > HtmlEscapeSymbols.MAX_ASCII_CHAR + && level < symbols.ESCAPE_LEVELS[HtmlEscapeSymbols.MAX_ASCII_CHAR + 1]) { + writer.write(c1); + continue; + } + + + /* + * Compute the codepoint. This will be used instead of the char for the rest of the process. + */ + final int codepoint = codePointAt((char)c1, (char)c2); + + + /* + * We know we need to escape, so from here on we will only work with the codepoint -- we can advance + * the chars. + */ + + if (Character.charCount(codepoint) > 1) { + // This is to compensate that we are actually reading two char positions with a single codepoint. + c1 = c2; + c2 = reader.read(); + } + + + /* + * ----------------------------------------------------------------------------------------- + * + * Perform the real escape, attending the different combinations of NCR, DCR and HCR needs. + * + * ----------------------------------------------------------------------------------------- + */ + + if (useNCRs) { + // We will try to use an NCR + + if (codepoint < symbols.NCRS_BY_CODEPOINT_LEN) { + // codepoint < 0x2fff - all HTML4, most HTML5 + + final short ncrIndex = symbols.NCRS_BY_CODEPOINT[codepoint]; + if (ncrIndex != symbols.NO_NCR) { + // There is an NCR for this codepoint! + writer.write(symbols.SORTED_NCRS[ncrIndex]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } else if (symbols.NCRS_BY_CODEPOINT_OVERFLOW != null) { + // codepoint >= 0x2fff. NCR, if exists, will live at the overflow map (if there is one). + + final Short ncrIndex = symbols.NCRS_BY_CODEPOINT_OVERFLOW.get(Integer.valueOf(codepoint)); + if (ncrIndex != null) { + writer.write(symbols.SORTED_NCRS[ncrIndex.shortValue()]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } + + } + + /* + * No NCR-escape was possible (or allowed), so we need decimal/hexa escape. + */ + + if (useHexa) { + writer.write(REFERENCE_HEXA_PREFIX); + writer.write(Integer.toHexString(codepoint)); + } else { + writer.write(REFERENCE_DECIMAL_PREFIX); + writer.write(String.valueOf(codepoint)); + } + writer.write(REFERENCE_SUFFIX); + + } + + } + + + + + + /* + * Perform an escape operation, based on char[], according to the specified level and type. + */ + static void escape(final char[] text, final int offset, final int len, final Writer writer, + final HtmlEscapeType escapeType, final HtmlEscapeLevel escapeLevel) + throws IOException { + + if (text == null || text.length == 0) { + return; + } + + final int level = escapeLevel.getEscapeLevel(); + final boolean useHtml5 = escapeType.getUseHtml5(); + final boolean useNCRs = escapeType.getUseNCRs(); + final boolean useHexa = escapeType.getUseHexa(); + + final HtmlEscapeSymbols symbols = + (useHtml5? HtmlEscapeSymbols.HTML5_SYMBOLS : HtmlEscapeSymbols.HTML4_SYMBOLS); + + final int max = (offset + len); + + int readOffset = offset; + + for (int i = offset; i < max; i++) { + + final char c = text[i]; + + + /* + * Shortcut: most characters will be ASCII/Alphanumeric, and we won't need to do anything at + * all for them + */ + if (c <= symbols.MAX_ASCII_CHAR && level < symbols.ESCAPE_LEVELS[c]) { + continue; + } + + + /* + * Shortcut: we might not want to escape non-ASCII chars at all either. + */ + if (c > symbols.MAX_ASCII_CHAR && level < symbols.ESCAPE_LEVELS[symbols.MAX_ASCII_CHAR + 1]) { + continue; + } + + + /* + * Compute the codepoint. This will be used instead of the char for the rest of the process. + */ + final int codepoint = Character.codePointAt(text, i); + + + /* + * At this point we know for sure we will need some kind of escape, so we + * can write all the contents pending up to this point. + */ + + if (i - readOffset > 0) { + writer.write(text, readOffset, (i - readOffset)); + } + + if (Character.charCount(codepoint) > 1) { + // This is to compensate that we are actually escaping two char[] positions with a single codepoint. + i++; + } + + readOffset = i + 1; + + + /* + * ----------------------------------------------------------------------------------------- + * + * Perform the real escape, attending the different combinations of NCR, DCR and HCR needs. + * + * ----------------------------------------------------------------------------------------- + */ + + if (useNCRs) { + // We will try to use an NCR + + if (codepoint < symbols.NCRS_BY_CODEPOINT_LEN) { + // codepoint < 0x2fff - all HTML4, most HTML5 + + final short ncrIndex = symbols.NCRS_BY_CODEPOINT[codepoint]; + if (ncrIndex != symbols.NO_NCR) { + // There is an NCR for this codepoint! + writer.write(symbols.SORTED_NCRS[ncrIndex]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } else if (symbols.NCRS_BY_CODEPOINT_OVERFLOW != null) { + // codepoint >= 0x2fff. NCR, if exists, will live at the overflow map (if there is one). + + final Short ncrIndex = symbols.NCRS_BY_CODEPOINT_OVERFLOW.get(Integer.valueOf(codepoint)); + if (ncrIndex != null) { + writer.write(symbols.SORTED_NCRS[ncrIndex.shortValue()]); + continue; + } // else, just let it exit the block and let decimal/hexa escape do its job + + } + + } + + /* + * No NCR-escape was possible (or allowed), so we need decimal/hexa escape. + */ + + if (useHexa) { + writer.write(REFERENCE_HEXA_PREFIX); + writer.write(Integer.toHexString(codepoint)); + } else { + writer.write(REFERENCE_DECIMAL_PREFIX); + writer.write(String.valueOf(codepoint)); + } + writer.write(REFERENCE_SUFFIX); + + } + + + /* + * ----------------------------------------------------------------------------------------------- + * Final cleaning: append the remaining unescaped text to the writer and return. + * ----------------------------------------------------------------------------------------------- + */ + + if (max - readOffset > 0) { + writer.write(text, readOffset, (max - readOffset)); + } + + } + + + + + + + /* + * This translation is needed during unescape to support ill-formed escape codes for Windows 1252 codes + * instead of the correct unicode ones (for example, € for the euro symbol instead of €). This is + * something browsers do support, and included in the HTML5 spec for consuming character references. + * See http://www.w3.org/TR/html5/syntax.html#consume-a-character-reference + */ + static int translateIllFormedCodepoint(final int codepoint) { + switch (codepoint) { + case 0x00: return 0xFFFD; + case 0x80: return 0x20AC; + case 0x82: return 0x201A; + case 0x83: return 0x0192; + case 0x84: return 0x201E; + case 0x85: return 0x2026; + case 0x86: return 0x2020; + case 0x87: return 0x2021; + case 0x88: return 0x02C6; + case 0x89: return 0x2030; + case 0x8A: return 0x0160; + case 0x8B: return 0x2039; + case 0x8C: return 0x0152; + case 0x8E: return 0x017D; + case 0x91: return 0x2018; + case 0x92: return 0x2019; + case 0x93: return 0x201C; + case 0x94: return 0x201D; + case 0x95: return 0x2022; + case 0x96: return 0x2013; + case 0x97: return 0x2014; + case 0x98: return 0x02DC; + case 0x99: return 0x2122; + case 0x9A: return 0x0161; + case 0x9B: return 0x203A; + case 0x9C: return 0x0153; + case 0x9E: return 0x017E; + case 0x9F: return 0x0178; + default: break; + } + if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { + return 0xFFFD; + } else if (codepoint > 0x10FFFF) { + return 0xFFFD; + } else { + return codepoint; + } + } + + + /* + * This methods (the two versions) are used instead of Integer.parseInt(str,radix) in order to avoid the need + * to create substrings of the text being unescaped to feed such method. + * - No need to check all chars are within the radix limits - reference parsing code will already have done so. + */ + + static int parseIntFromReference(final String text, final int start, final int end, final int radix) { + int result = 0; + for (int i = start; i < end; i++) { + final char c = text.charAt(i); + int n = -1; + for (int j = 0; j < HEXA_CHARS_UPPER.length; j++) { + if (c == HEXA_CHARS_UPPER[j] || c == HEXA_CHARS_LOWER[j]) { + n = j; + break; + } + } + result *= radix; + if (result < 0) { + return 0xFFFD; + } + result += n; + if (result < 0) { + return 0xFFFD; + } + } + return result; + } + + static int parseIntFromReference(final char[] text, final int start, final int end, final int radix) { + int result = 0; + for (int i = start; i < end; i++) { + final char c = text[i]; + int n = -1; + for (int j = 0; j < HEXA_CHARS_UPPER.length; j++) { + if (c == HEXA_CHARS_UPPER[j] || c == HEXA_CHARS_LOWER[j]) { + n = j; + break; + } + } + result *= radix; + if (result < 0) { + return 0xFFFD; + } + result += n; + if (result < 0) { + return 0xFFFD; + } + } + return result; + } + + + + + + + + + + /* + * Perform an unescape operation based on String. Unescape operations are always based on the HTML5 symbol set. + * Unescape operations will be performed in the most similar way possible to the process a browser follows for + * showing HTML5 escaped code. See: http://www.w3.org/TR/html5/syntax.html#consume-a-character-reference + */ + static String unescape(final String text) { + + if (text == null) { + return null; + } + + // Unescape will always cover the full HTML5 spectrum. + final HtmlEscapeSymbols symbols = HtmlEscapeSymbols.HTML5_SYMBOLS; + StringBuilder strBuilder = null; + + final int offset = 0; + final int max = text.length(); + + int readOffset = offset; + int referenceOffset = offset; + + for (int i = offset; i < max; i++) { + + final char c = text.charAt(i); + + /* + * Check the need for an unescape operation at this point + */ + + if (c != REFERENCE_PREFIX || (i + 1) >= max) { + continue; + } + + int codepoint = 0; + + if (c == REFERENCE_PREFIX) { + + final char c1 = text.charAt(i + 1); + + if (c1 == '\u0020' || // SPACE + c1 == '\n' || // LF + c1 == '\u0009' || // TAB + c1 == '\u000C' || // FF + c1 == '\u003C' || // LES-THAN SIGN + c1 == '\u0026') { // AMPERSAND + // Not a character references. No characters are consumed, and nothing is returned. + continue; + + } else if (c1 == REFERENCE_NUMERIC_PREFIX2) { + + if (i + 2 >= max) { + // No reference possible + continue; + } + + final char c2 = text.charAt(i + 2); + + if ((c2 == REFERENCE_HEXA_PREFIX3_LOWER || c2 == REFERENCE_HEXA_PREFIX3_UPPER) && (i + 3) < max) { + // This is a hexadecimal reference + + int f = i + 3; + while (f < max) { + final char cf = text.charAt(f); + if (!((cf >= '0' && cf <= '9') || (cf >= 'A' && cf <= 'F') || (cf >= 'a' && cf <= 'f'))) { + break; + } + f++; + } + + if ((f - (i + 3)) <= 0) { + // We weren't able to consume any hexa chars + continue; + } + + codepoint = parseIntFromReference(text, i + 3, f, 16); + referenceOffset = f - 1; + + if ((f < max) && text.charAt(f) == REFERENCE_SUFFIX) { + referenceOffset++; + } + + codepoint = translateIllFormedCodepoint(codepoint); + + // Don't continue here, just let the unescape code below do its job + + } else if (c2 >= '0' && c2 <= '9') { + // This is a decimal reference + + int f = i + 2; + while (f < max) { + final char cf = text.charAt(f); + if (!(cf >= '0' && cf <= '9')) { + break; + } + f++; + } + + if ((f - (i + 2)) <= 0) { + // We weren't able to consume any decimal chars + continue; + } + + codepoint = parseIntFromReference(text, i + 2, f, 10); + referenceOffset = f - 1; + + if ((f < max) && text.charAt(f) == REFERENCE_SUFFIX) { + referenceOffset++; + } + + codepoint = translateIllFormedCodepoint(codepoint); + + // Don't continue here, just let the unescape code below do its job + + } else { + // This is not a valid reference, just discard + continue; + } + + + } else { + + // This is a named reference, must be comprised only of ALPHABETIC chars + + int f = i + 1; + while (f < max) { + final char cf = text.charAt(f); + if (!((cf >= 'a' && cf <= 'z') || (cf >= 'A' && cf <= 'Z') || (cf >= '0' && cf <= '9'))) { + break; + } + f++; + } + + if ((f - (i + 1)) <= 0) { + // We weren't able to consume any alphanumeric + continue; + } + + if ((f < max) && text.charAt(f) == REFERENCE_SUFFIX) { + f++; + } + + final int ncrPosition = HtmlEscapeSymbols.binarySearch(symbols.SORTED_NCRS, text, i, f); + if (ncrPosition >= 0) { + codepoint = symbols.SORTED_CODEPOINTS[ncrPosition]; + } else if (ncrPosition == Integer.MIN_VALUE) { + // Not found! Just ignore our efforts to find a match. + continue; + } else if (ncrPosition < -10) { + // Found but partial! + final int partialIndex = (-1) * (ncrPosition + 10); + final char[] partialMatch = symbols.SORTED_NCRS[partialIndex]; + codepoint = symbols.SORTED_CODEPOINTS[partialIndex]; + f -= ((f - i) - partialMatch.length); // un-consume the chars remaining from the partial match + } else { + // Should never happen! + throw new RuntimeException("Invalid unescape codepoint after search: " + ncrPosition); + } + + referenceOffset = f - 1; + + } + + } + + + /* + * At this point we know for sure we will need some kind of unescape, so we + * can increase the offset and initialize the string builder if needed, along with + * copying to it all the contents pending up to this point. + */ + + if (strBuilder == null) { + strBuilder = new StringBuilder(max + 5); + } + + if (i - readOffset > 0) { + strBuilder.append(text, readOffset, i); + } + + i = referenceOffset; + readOffset = i + 1; + + /* + * -------------------------- + * + * Perform the real unescape + * + * -------------------------- + */ + + if (codepoint > '\uFFFF') { + strBuilder.append(Character.toChars(codepoint)); + } else if (codepoint < 0) { + // This is a double-codepoint unescape operation + final int[] codepoints = symbols.DOUBLE_CODEPOINTS[((-1) * codepoint) - 1]; + if (codepoints[0] > '\uFFFF') { + strBuilder.append(Character.toChars(codepoints[0])); + } else { + strBuilder.append((char) codepoints[0]); + } + if (codepoints[1] > '\uFFFF') { + strBuilder.append(Character.toChars(codepoints[1])); + } else { + strBuilder.append((char) codepoints[1]); + } + } else { + strBuilder.append((char)codepoint); + } + + } + + + /* + * ----------------------------------------------------------------------------------------------- + * Final cleaning: return the original String object if no unescape was actually needed. Otherwise + * append the remaining escaped text to the string builder and return. + * ----------------------------------------------------------------------------------------------- + */ + + if (strBuilder == null) { + return text; + } + + if (max - readOffset > 0) { + strBuilder.append(text, readOffset, max); + } + + return strBuilder.toString(); + + } + + + + + + + /* + * Perform an unescape operation based on a Reader, writing the results to a Writer. Unescape operations are + * always based on the HTML5 symbol set. Unescape operations will be performed in the most similar way + * possible to the process a browser follows for showing HTML5 escaped code. + * See: http://www.w3.org/TR/html5/syntax.html#consume-a-character-reference + * + * Note this reader is going to be read char-by-char, so some kind of buffering might be appropriate if this + * is an inconvenience for the specific Reader implementation. + */ + static void unescape(final Reader reader, final Writer writer) throws IOException { + + if (reader == null) { + return; + } + + // Unescape will always cover the full HTML5 spectrum. + final HtmlEscapeSymbols symbols = HtmlEscapeSymbols.HTML5_SYMBOLS; + + char[] escapes = new char[10]; + int escapei = 0; + + int c1, c2, ce; // c1: current char, c2: next char, ce: current escaped char + + c2 = reader.read(); + + while (c2 >= 0) { + + c1 = c2; + c2 = reader.read(); + + escapei = 0; + + /* + * Check the need for an unescape operation at this point + */ + + if (c1 != REFERENCE_PREFIX || c2 < 0) { + writer.write(c1); + continue; + } + + int codepoint = 0; + + if (c1 == REFERENCE_PREFIX) { + + if (c2 == '\u0020' || // SPACE + c2 == '\n' || // LF + c2 == '\u0009' || // TAB + c2 == '\u000C' || // FF + c2 == '\u003C' || // LES-THAN SIGN + c2 == '\u0026') { // AMPERSAND + // Not a character references. No characters are consumed, and nothing is returned. + writer.write(c1); + continue; + + } else if (c2 == REFERENCE_NUMERIC_PREFIX2) { + + final int c3 = reader.read(); + + if (c3 < 0) { + // No reference possible + writer.write(c1); + writer.write(c2); + c1 = c2; + c2 = c3; + continue; + } + + if ((c3 == REFERENCE_HEXA_PREFIX3_LOWER || c3 == REFERENCE_HEXA_PREFIX3_UPPER)) { + // This is a hexadecimal reference + + ce = reader.read(); + while (ce >= 0) { + if (!((ce >= '0' && ce <= '9') || (ce >= 'A' && ce <= 'F') || (ce >= 'a' && ce <= 'f'))) { + break; + } + if (escapei == escapes.length) { + // too many escape chars for our array: grow it! + final char[] newEscapes = new char[escapes.length + 4]; + System.arraycopy(escapes, 0, newEscapes, 0, escapes.length); + escapes = newEscapes; + } + escapes[escapei] = (char) ce; + ce = reader.read(); + escapei++; + } + + if (escapei == 0) { + // We weren't able to consume any hexa chars + writer.write(c1); + writer.write(c2); + writer.write(c3); + c1 = c3; + c2 = ce; + continue; + } + + c1 = escapes[escapei - 1]; + c2 = ce; + + codepoint = parseIntFromReference(escapes, 0, escapei, 16); + + if (c2 == REFERENCE_SUFFIX) { + // If the reference ends in a ';', just consume it + c1 = c2; + c2 = reader.read(); + } + + codepoint = translateIllFormedCodepoint(codepoint); + + escapei = 0; + + // Don't continue here, just let the unescape code below do its job + + } else if (c3 >= '0' && c3 <= '9') { + // This is a decimal reference + + ce = c3; + while (ce >= 0) { + if (!(ce >= '0' && ce <= '9')) { + break; + } + if (escapei == escapes.length) { + // too many escape chars for our array: grow it! + final char[] newEscapes = new char[escapes.length + 4]; + System.arraycopy(escapes, 0, newEscapes, 0, escapes.length); + escapes = newEscapes; + } + escapes[escapei] = (char) ce; + ce = reader.read(); + escapei++; + } + + if (escapei == 0) { + // We weren't able to consume any decimal chars + writer.write(c1); + writer.write(c2); + c1 = c2; + c2 = c3; + continue; + } + + c1 = escapes[escapei - 1]; + c2 = ce; + + codepoint = parseIntFromReference(escapes, 0, escapei, 10); + + if (c2 == REFERENCE_SUFFIX) { + // If the reference ends in a ';', just consume it + c1 = c2; + c2 = reader.read(); + } + + codepoint = translateIllFormedCodepoint(codepoint); + + escapei = 0; + + // Don't continue here, just let the unescape code below do its job + + } else { + // This is not a valid reference, just discard + writer.write(c1); + writer.write(c2); + c1 = c2; + c2 = c3; + continue; + } + + + } else { + + // This is a named reference, must be comprised only of ALPHABETIC chars + + ce = c2; + while (ce >= 0) { + if (!((ce >= '0' && ce <= '9') || (ce >= 'A' && ce <= 'Z') || (ce >= 'a' && ce <= 'z'))) { + break; + } + if (escapei == escapes.length) { + // too many escape chars for our array: grow it! + final char[] newEscapes = new char[escapes.length + 4]; + System.arraycopy(escapes, 0, newEscapes, 0, escapes.length); + escapes = newEscapes; + } + escapes[escapei] = (char) ce; + ce = reader.read(); + escapei++; + } + + if (escapei == 0) { + // We weren't able to consume any decimal chars + writer.write(c1); + continue; + } + + if (escapei + 2 >= escapes.length) { + // the entire escape sequence does not fit: grow it! + final char[] newEscapes = new char[escapes.length + 4]; + System.arraycopy(escapes, 0, newEscapes, 0, escapes.length); + escapes = newEscapes; + } + + System.arraycopy(escapes, 0, escapes, 1, escapei); + escapes[0] = (char) c1; + escapei++; + + if (ce == REFERENCE_SUFFIX) { + // If the reference ends in a ';', just consume it + escapes[escapei++] = (char) ce; + ce = reader.read(); + } + + c1 = escapes[escapei - 1]; + c2 = ce; + + final int ncrPosition = HtmlEscapeSymbols.binarySearch(symbols.SORTED_NCRS, escapes, 0, escapei); + if (ncrPosition >= 0) { + codepoint = symbols.SORTED_CODEPOINTS[ncrPosition]; + escapei = 0; + } else if (ncrPosition == Integer.MIN_VALUE) { + // Not found! Just ignore our efforts to find a match. + writer.write(escapes, 0, escapei); + continue; + } else if (ncrPosition < -10) { + // Found but partial! + final int partialIndex = (-1) * (ncrPosition + 10); + final char[] partialMatch = symbols.SORTED_NCRS[partialIndex]; + codepoint = symbols.SORTED_CODEPOINTS[partialIndex]; + System.arraycopy(escapes, partialMatch.length, escapes, 0, (escapei - partialMatch.length)); + escapei -= partialMatch.length; // so that we know we have to output the rest of 'escapes' + } else { + // Should never happen! + throw new RuntimeException("Invalid unescape codepoint after search: " + ncrPosition); + } + + } + + } + + /* + * -------------------------- + * + * Perform the real unescape + * + * -------------------------- + */ + + if (codepoint > '\uFFFF') { + writer.write(Character.toChars(codepoint)); + } else if (codepoint < 0) { + // This is a double-codepoint unescape operation + final int[] codepoints = symbols.DOUBLE_CODEPOINTS[((-1) * codepoint) - 1]; + if (codepoints[0] > '\uFFFF') { + writer.write(Character.toChars(codepoints[0])); + } else { + writer.write((char) codepoints[0]); + } + if (codepoints[1] > '\uFFFF') { + writer.write(Character.toChars(codepoints[1])); + } else { + writer.write((char) codepoints[1]); + } + } else { + writer.write((char)codepoint); + } + + /* + * ---------------------------------------- + * Cleanup, in case we had a partial match + * ---------------------------------------- + */ + + if (escapei > 0) { + writer.write(escapes, 0, escapei); + escapei = 0; + } + + + } + + } + + + + + + + /* + * Perform an unescape operation based on char[]. Unescape operations are always based on the HTML5 symbol set. + * Unescape operations will be performed in the most similar way possible to the process a browser follows for + * showing HTML5 escaped code. See: http://www.w3.org/TR/html5/syntax.html#consume-a-character-reference + */ + static void unescape(final char[] text, final int offset, final int len, final Writer writer) + throws IOException { + + if (text == null) { + return; + } + + final HtmlEscapeSymbols symbols = HtmlEscapeSymbols.HTML5_SYMBOLS; + + final int max = (offset + len); + + int readOffset = offset; + int referenceOffset = offset; + + for (int i = offset; i < max; i++) { + + final char c = text[i]; + + /* + * Check the need for an unescape operation at this point + */ + + if (c != REFERENCE_PREFIX || (i + 1) >= max) { + continue; + } + + int codepoint = 0; + + if (c == REFERENCE_PREFIX) { + + final char c1 = text[i + 1]; + + if (c1 == '\u0020' || // SPACE + c1 == '\n' || // LF + c1 == '\u0009' || // TAB + c1 == '\u000C' || // FF + c1 == '\u003C' || // LES-THAN SIGN + c1 == '\u0026') { // AMPERSAND + // Not a character references. No characters are consumed, and nothing is returned. + continue; + + } else if (c1 == REFERENCE_NUMERIC_PREFIX2) { + + if (i + 2 >= max) { + // No reference possible + continue; + } + + final char c2 = text[i + 2]; + + if ((c2 == REFERENCE_HEXA_PREFIX3_LOWER || c2 == REFERENCE_HEXA_PREFIX3_UPPER) && (i + 3) < max) { + // This is a hexadecimal reference + + int f = i + 3; + while (f < max) { + final char cf = text[f]; + if (!((cf >= '0' && cf <= '9') || (cf >= 'A' && cf <= 'F') || (cf >= 'a' && cf <= 'f'))) { + break; + } + f++; + } + + if ((f - (i + 3)) <= 0) { + // We weren't able to consume any hexa chars + continue; + } + + codepoint = parseIntFromReference(text, i + 3, f, 16); + referenceOffset = f - 1; + + if ((f < max) && text[f] == REFERENCE_SUFFIX) { + referenceOffset++; + } + + codepoint = translateIllFormedCodepoint(codepoint); + + // Don't continue here, just let the unescape code below do its job + + } else if (c2 >= '0' && c2 <= '9') { + // This is a decimal reference + + int f = i + 2; + while (f < max) { + final char cf = text[f]; + if (!(cf >= '0' && cf <= '9')) { + break; + } + f++; + } + + if ((f - (i + 2)) <= 0) { + // We weren't able to consume any decimal chars + continue; + } + + codepoint = parseIntFromReference(text, i + 2, f, 10); + referenceOffset = f - 1; + + if ((f < max) && text[f] == REFERENCE_SUFFIX) { + referenceOffset++; + } + + codepoint = translateIllFormedCodepoint(codepoint); + + // Don't continue here, just let the unescape code below do its job + + } else { + // This is not a valid reference, just discard + continue; + } + + + } else { + + // This is a named reference, must be comprised only of ALPHABETIC chars + + int f = i + 1; + while (f < max) { + final char cf = text[f]; + if (!((cf >= 'a' && cf <= 'z') || (cf >= 'A' && cf <= 'Z') || (cf >= '0' && cf <= '9'))) { + break; + } + f++; + } + + if ((f - (i + 1)) <= 0) { + // We weren't able to consume any alphanumeric + continue; + } + + if ((f < max) && text[f] == REFERENCE_SUFFIX) { + f++; + } + + final int ncrPosition = HtmlEscapeSymbols.binarySearch(symbols.SORTED_NCRS, text, i, f); + if (ncrPosition >= 0) { + codepoint = symbols.SORTED_CODEPOINTS[ncrPosition]; + } else if (ncrPosition == Integer.MIN_VALUE) { + // Not found! Just ignore our efforts to find a match. + continue; + } else if (ncrPosition < -10) { + // Found but partial! + final int partialIndex = (-1) * (ncrPosition + 10); + final char[] partialMatch = symbols.SORTED_NCRS[partialIndex]; + codepoint = symbols.SORTED_CODEPOINTS[partialIndex]; + f -= ((f - i) - partialMatch.length); // un-consume the chars remaining from the partial match + } else { + // Should never happen! + throw new RuntimeException("Invalid unescape codepoint after search: " + ncrPosition); + } + + referenceOffset = f - 1; + + } + + } + + + /* + * At this point we know for sure we will need some kind of unescape, so we + * write all the contents pending up to this point. + */ + + if (i - readOffset > 0) { + writer.write(text, readOffset, (i - readOffset)); + } + + i = referenceOffset; + readOffset = i + 1; + + /* + * -------------------------- + * + * Perform the real unescape + * + * -------------------------- + */ + + if (codepoint > '\uFFFF') { + writer.write(Character.toChars(codepoint)); + } else if (codepoint < 0) { + // This is a double-codepoint unescape operation + final int[] codepoints = symbols.DOUBLE_CODEPOINTS[((-1) * codepoint) - 1]; + if (codepoints[0] > '\uFFFF') { + writer.write(Character.toChars(codepoints[0])); + } else { + writer.write((char) codepoints[0]); + } + if (codepoints[1] > '\uFFFF') { + writer.write(Character.toChars(codepoints[1])); + } else { + writer.write((char) codepoints[1]); + } + } else { + writer.write((char) codepoint); + } + + } + + + /* + * ----------------------------------------------------------------------------------------------- + * Final cleaning: writer the remaining escaped text and return. + * ----------------------------------------------------------------------------------------------- + */ + + if (max - readOffset > 0) { + writer.write(text, readOffset, (max - readOffset)); + } + + } + + + + + private static int codePointAt(final char c1, final char c2) { + if (Character.isHighSurrogate(c1)) { + if (c2 >= 0) { + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } + } + } + return c1; + } + + + +} + diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/CommandExecuter.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/CommandExecuter.java index d9a82bb..f6a0c80 100755 --- a/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/CommandExecuter.java +++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/CommandExecuter.java @@ -5,7 +5,8 @@ package ohi.andre.consolelauncher.tuils.interfaces; */ public interface CommandExecuter { - String exec(String aliasValue, String aliasName); - String exec(String input); - String exec(String input, boolean needWriteInput); + void exec(String aliasValue, String aliasName); + void exec(String input); + void exec(String input, boolean needWriteInput); + void exec(String input, Object obj); } diff --git a/app/src/main/res/menu/notification_menu.xml b/app/src/main/res/menu/notification_menu.xml new file mode 100644 index 0000000..62cd80c --- /dev/null +++ b/app/src/main/res/menu/notification_menu.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@+id/exclude_app" android:title="@string/exclude_app"/> + <item android:id="@+id/exclude_notification" android:title="@string/exclude_notification"/> +</menu> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2241b0b..4467be3 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -6,18 +6,22 @@ <string name="location_off">Turn on GPS or network location</string> <string name="share_label">Share</string> <string name="output_about">Francesco Andreuzzi (Italy)\n\t-> Programmer - \n\nLuke Winward (USA)\n\t-> Idea and design - \n\nAlexander King (Australia)\n\t-> t-ui theme website and db management</string> + \n\nLuke Winward (USA)\n\t-> Idea and design</string> <string name="output_refresh">Refresh: apps, alias, music, contacts, rss feeds</string> <string name="output_rate">Thank you!</string> <string name="start_notification">T-UI started</string> <string name="tui_running">T-UI is running</string> <string name="anr">t-ui is having some issues. Please send to the developer your file crash.txt in the "t-ui" folder</string> <string name="admin_permission">Lock the screen on double tap</string> + <string name="translation_not_found">Translation not found</string> + + <!-- notifications menu --> + <string name="exclude_notification">Exclude this notification</string> + <string name="exclude_app">Exclude app</string> + <string name="notification_service_not_running">The notification service is\'t running</string> <!-- regex --> - <string name="regex_not_found">Couldn\'t find a Regex with this ID</string> - <string name="regex_exists">An other Regex is already using this ID</string> + <string name="invalid_regex">Invalid regex:</string> <!-- theme --> <string name="theme_not_found">Theme not found</string> @@ -57,6 +61,7 @@ <string name="hint_config">Are you having doubts about any config option? Use the cmd \"$ config -tutorial\" and look for that option inside the table</string> <string name="hint_alias">Did you know that t-ui supports aliases? It also supports parameterized aliases.\nFor more info, use the cmd \"$ alias -tutorial\"</string> <string name="hint_musicdisable">Do you want to make t-ui lighter? Use the cmd \"$ config -set enable_music false\" to disable the music player and save resources</string> + <string name="hint_excludenotification">Did you know? You can exclude an unwanted notification long-clicking on it</string> <string name="hint_disable">You can disable this messages with the cmd \"$ config -set show_hints false\"</string> @@ -91,7 +96,11 @@ <string name="rss_not_found">Invalid ID: no RSS feed found</string> <string name="id_already">This ID has already been used</string> <string name="id_notfound">Invalid ID</string> - + <string name="rss_never_checked">This RSS feed hasn\'t been checked yet</string> + <string name="rss_invalid_timeformat">The time format couldn\'t be parsed</string> + <string name="rss_invalid_date">Invalid publication date tag:</string> + <string name="rss_invalid_entry_tag">Invalid entry tag:</string> + <string name="rss_invalid_empty">Invalid RSS: the file is empty:</string> <!-- alias --> <string name="output_aliasnotfound">Alias not found:</string> @@ -189,9 +198,10 @@ </string> <string name="help_config">-set [option] [value] -> set the value of an option \n-append [option] [value] -> append the given string to the value of the selected option - \n-file [file] -> open a config file + \n-file [file] -> open a config file (behavior.xml, ui.xml, theme.xml, ...) \n-get [option] -> get the value of an option \n-reset [option] -> reset the value of an option + \n-info [option] -> request info about the given option \n-apply [file] -> move a file to the t-ui directory \n-tutorial -> open the tutorial page </string> @@ -268,12 +278,15 @@ \n$ beep</string> <string name="help_notifications">-inc [appName] -> include an application \n-exc [appName] -> exclude an application - \n-clr [color] [appName] -> set the color to be used for the application + \n-color [color] [appName] -> set the color to be used for the application + \n-format [ID] [appName] -> apply the format with the given ID to the corresponding application + \n-add_filter [ID] [filter] -> create a new filter with the given ID + \n-add_format [ID] [format] -> create a new format with the given ID + \n-rm_filter [ID] -> delete the filter having the given ID + \n-rm_format [ID] -> delete the format having the given ID + \n-access -> show the system settings page dedicated to the Notification Access \n-file -> open notifications.xml - \n-title_filter [ID] [regex] -> set a filter over notifications title with a given ID - \n-text_filter [ID] [regex] -> set a filter over notifications text with a given ID - \n-apply_filter [ID] [appName] -> apply a filter (or a group of filters) to a single application - \n-access -> show the system settings page dedicated to the Notification Access</string> + \n-tutorial -> show the tutorial page</string> <string name="help_location">Show your current location</string> <string name="help_cntcts">-ls -> list your contacts \n-l [contactName] -> show details about a contact @@ -286,18 +299,22 @@ \n-ls -> list your RSS feeds \n-l [ID] -> show the whole content of the corresponding RSS feed \n-show [ID] [boolean] -> toggle the [show] property of the given saved RSS feed - \n-time [ID] [update_time_in_seconds] -> set the update time of the corresponding RSS feed + \n-update_time [ID] [update_time_in_seconds] -> set the update time of the corresponding RSS feed \n-format [ID] [format] -> set the format of the corresponding RSS feed ([format] can be both a string or the ID of an existing format) \n-color [ID] [color] -> set the base color of the corresponding RSS feed \n-wifi_only [ID] [boolean] -> if [boolean] is true, the corresponding feed will be updated only if your device is connected to a WiFi network \n-last_check [ID] -> show the last time t-ui checked this RSS feed - \n-frc [ID] -> update the corresponding RSS feed and eventually show new updates + \n-entry_tag [ID] -> set the base tag for every single entry (default: \"item\") + \n-date_tag [ID] [tag] -> set the tag which holds info about the publication date (default: \"pubDate\") + \n-add_command [ID] [IDs (comma separated)] [regex] [cmd] -> create a new regex command that will be applied on the RSS feeds corresponding to IDs + \n-rm_command [ID] -> rm the corresponding regex command + \n-frc [ID] -> update the corresponding RSS feed \n-info [ID] -> show info about the corresponding RSS feed settings \n-include_if_matches [ID] [regex] -> if the given regex matches on an RSS feed item, it will be shown ([regex] can be both a string or the ID of a regex saved inside regex.xml) \n-exclude_if_matches [ID] [regex] -> if the given regex matches on an RSS feed item, it won\'t be shown ([regex] can be both a string or the ID of a regex saved inside regex.xml) \n-add_format [ID] [format] -> create a new format that you will be able to use for more than one RSS feed \n-rm_format [ID] [format] -> remove an existing format - \n-file -> open the file RSS.xml</string> + \n-file -> open the file rss.xml</string> <!-- linux --> <string name="help_ctrlc">Interrupt the current shell process and create a new one</string> diff --git a/build.gradle b/build.gradle index 5bbec01..8682043 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ task clean(type: Delete) { allprojects { repositories { + jcenter() maven { url "https://jitpack.io" } } } -- GitLab