diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index aa26825..2db19ba 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + - + + + + + diff --git a/app/src/main/java/com/maxistar/textpad/activities/EditorActivity.java b/app/src/main/java/com/maxistar/textpad/activities/EditorActivity.java index 94591af..e82f927 100644 --- a/app/src/main/java/com/maxistar/textpad/activities/EditorActivity.java +++ b/app/src/main/java/com/maxistar/textpad/activities/EditorActivity.java @@ -39,10 +39,12 @@ import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.View; import android.view.inputmethod.EditorInfo; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; +import android.widget.ProgressBar; import android.widget.ScrollView; import android.widget.SearchView; import android.widget.Toast; @@ -53,6 +55,7 @@ import com.maxistar.textpad.ServiceLocator; import com.maxistar.textpad.service.SettingsService; import com.maxistar.textpad.TPStrings; +import com.maxistar.textpad.tts.Dictator; import com.maxistar.textpad.service.AlternativeUrlsService; import com.maxistar.textpad.service.RecentFilesService; import com.maxistar.textpad.service.ThemeService; @@ -106,7 +109,7 @@ public class EditorActivity extends AppCompatActivity { Uri lastTriedSystemUri = null; - + boolean changed = false; boolean exitDialogShown = false; @@ -309,7 +312,7 @@ public void onSaveInstanceState(@NonNull Bundle outState) { protected void onStop() { super.onStop(); } - + @Override public void onBackPressed() { if (this.changed && !exitDialogShown) { @@ -475,7 +478,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { MenuItem redoMenu = menu.findItem(R.id.menu_edit_redo); redoMenu.setEnabled(editTextUndoRedo.getCanRedo()); - + updateRecentFiles(menu); if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { @@ -608,6 +611,8 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { openRecentFile(4); } else if (itemId == R.id.menu_document_new) { newFile(); + } else if (itemId == R.id.menu_dictate) { + startDictation(); } else if (itemId == R.id.menu_document_save) { saveFile(); } else if (itemId == R.id.menu_document_save_as) { @@ -709,6 +714,13 @@ private void showSettings() { } } + private void startDictation() { + ProgressBar view = findViewById(R.id.speechProgressBar); + view.setVisibility(View.VISIBLE); + Dictator dictator = new Dictator(); + dictator.startDictation(mText, this, view); + } + private void showSettingsActivity() { Intent intent = new Intent(this.getBaseContext(), SettingsActivity.class); @@ -859,7 +871,7 @@ protected void exitApplication() { System.exitFromApp(EditorActivity.this); } } - + protected void selectFileUsingAndroidSystemPicker() { Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); diff --git a/app/src/main/java/com/maxistar/textpad/nlp/tokenizer/GenericTokenizer.java b/app/src/main/java/com/maxistar/textpad/nlp/tokenizer/GenericTokenizer.java new file mode 100644 index 0000000..2484411 --- /dev/null +++ b/app/src/main/java/com/maxistar/textpad/nlp/tokenizer/GenericTokenizer.java @@ -0,0 +1,80 @@ +package com.maxistar.textpad.nlp.tokenizer; + +import java.util.ArrayList; + +public class GenericTokenizer { + final static int MODE_DEFAULT = 0; + final static int MODE_PUNCTUATION = 1; + final static int MODE_WHITESPACE = 2; + final static int MODE_WORD = 3; + + public String[] tokenizeString(String input) { + int length = input.length(); + int mode = MODE_DEFAULT; + ArrayList result = new ArrayList<>(); + StringBuilder currentWord = new StringBuilder(); + for (int i = 0; i < length; i++) { + String currentChar = input.substring(i, i + 1); + if (mode == MODE_WORD) { + if (isLetter(currentChar)) { + currentWord.append(currentChar); + } else if (isPunctuation(currentChar)) { + result.add(currentWord.toString()); + result.add(currentChar); + mode = MODE_PUNCTUATION; + currentWord = new StringBuilder(); + } else if (isWhitespace(currentChar)) { + result.add(currentWord.toString()); + result.add(currentChar); + mode = MODE_WHITESPACE; + currentWord = new StringBuilder(); + } + } else if (mode == MODE_PUNCTUATION) { + if (isLetter(currentChar)) { + currentWord.append(currentChar); + mode = MODE_WORD; + } else if (isPunctuation(currentChar)) { + result.add(currentChar); + } else if (isWhitespace(currentChar)) { + result.add(currentChar); + mode = MODE_WHITESPACE; + } + } else if (mode == MODE_WHITESPACE) { + if (isLetter(currentChar)) { + currentWord.append(currentChar); + mode = MODE_WORD; + } else if (isPunctuation(currentChar)) { + result.add(currentChar); + mode = MODE_PUNCTUATION; + } else if (isWhitespace(currentChar)) { + result.add(currentChar); + } + } else { + if (isLetter(currentChar)) { + currentWord.append(currentChar); + mode = MODE_WORD; + } else if (isPunctuation(currentChar)) { + result.add(currentChar); + mode = MODE_PUNCTUATION; + } else if (isWhitespace(currentChar)) { + result.add(currentChar); + mode = MODE_WHITESPACE; + } + } + } + String[] res = new String[result.size()]; + return result.toArray(res); + } + + private boolean isWhitespace(String currentChar) { + return currentChar.equals(" ") || currentChar.equals("\n"); + } + + private boolean isPunctuation(String currentChar) { + return currentChar.matches("[—?,:;.…!«»)(]"); + } + + private boolean isLetter(String currentChar) { + return currentChar.matches("[a-zA-Zа-яА-Я\\-]"); + } +} diff --git a/app/src/main/java/com/maxistar/textpad/tts/Dictator.java b/app/src/main/java/com/maxistar/textpad/tts/Dictator.java new file mode 100644 index 0000000..bdec3b5 --- /dev/null +++ b/app/src/main/java/com/maxistar/textpad/tts/Dictator.java @@ -0,0 +1,386 @@ +package com.maxistar.textpad.tts; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +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.Looper; +import android.speech.RecognitionListener; +import android.speech.RecognizerIntent; +import android.speech.SpeechRecognizer; +import android.speech.tts.TextToSpeech; +import android.speech.tts.UtteranceProgressListener; +import android.text.Editable; +import android.text.Spanned; +import android.text.style.BackgroundColorSpan; +import android.util.Log; +import android.view.View; +import android.widget.EditText; +import android.widget.ProgressBar; +import android.widget.Toast; + +import com.maxistar.textpad.R; + +import java.util.ArrayList; +import java.util.Locale; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +public class Dictator implements RecognitionListener { + + private final String LOG_TAG = "VoiceRecognition"; + + private ProgressBar speechProgressBar; + + private SpeechRecognizer speech = null; + + private EditText mText; + + VoiceReader voiceReader; + + public Dictator() { + voiceReader = new VoiceReader(); + } + + Context context; + + String lastDictation = ""; + + static final int COMMAND_MODE = 0; + static final int DICTATION_MODE = 1; + + static final int COMMAND_NONE = 0; + static final int COMMAND_UNKNOWN = 1; + static final int COMMAND_DICTATE = 2; + static final int COMMAND_EXIT = 3; + static final int COMMAND_REPEAT = 4; + static final int COMMAND_SELECT_SENTENCE = 5; + + int mode = 0; + int lastCommand = 0; + + public void startDictation(EditText mText, Activity context, ProgressBar speechProgressBar) { + + this.context = context; + + + this.speechProgressBar = speechProgressBar; + this.mText = mText; + checkPermissions(context); + + speech = SpeechRecognizer.createSpeechRecognizer(context); + speech.setRecognitionListener(this); + //startHearing(context); + + sayWaitingForCommand(); + } + + public void sayWaitingForCommand() { + sayMessage(R.string.tts_waiting_the_command); + } + + + public void sayReady() { + sayMessage(R.string.tts_ready_to_dictate); + } + + public void sayMessage(int stringId) { + sayString(context.getResources().getString(stringId)); + } + + public void sayString(String str) { + Locale locale = Locale.getDefault(); + String timeStr = String.valueOf(System.currentTimeMillis()); + voiceReader.speak(str, "somerandomkey" + timeStr, locale); + } + + public void startHearing(Context context) { + Intent recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + String language = "ru"; + recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, language); + recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); + recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName()); + recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 10); + speech.startListening(recognizerIntent); + } + + private void checkPermissions(Activity context) { + if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) { + return; + } + + if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.RECORD_AUDIO)) { + Toast.makeText(context, "", Toast.LENGTH_SHORT).show(); + } else { + ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.RECORD_AUDIO}, 1); + } + } + + @Override + public void onReadyForSpeech(Bundle bundle) { + Log.i(LOG_TAG, "onReadyForSpeech"); + } + + @Override + public void onBeginningOfSpeech() { + speechProgressBar.setVisibility(View.VISIBLE); + Log.i(LOG_TAG, "onBeginningOfSpeech"); + speechProgressBar.setIndeterminate(false); + speechProgressBar.setMax(10); + } + + @Override + public void onRmsChanged(float rmsdB) { + Log.i(LOG_TAG, "onRmsChanged: " + rmsdB); + speechProgressBar.setProgress((int) rmsdB); + } + + @Override + public void onBufferReceived(byte[] bytes) { + Log.i(LOG_TAG, "onBufferReceived: " + bytes); + } + + @Override + public void onEndOfSpeech() { + Log.i(LOG_TAG, "onEndOfSpeech"); + speechProgressBar.setIndeterminate(true); + //toggleButton.setChecked(false); + } + + @Override + public void onError(int errorCode) { + speechProgressBar.setVisibility(View.INVISIBLE); + String errorMessage = getErrorText(errorCode); + Log.d(LOG_TAG, "FAILED " + errorMessage); + } + + @Override + public void onResults(Bundle results) { + speechProgressBar.setVisibility(View.INVISIBLE); + Log.i("LOG", "onResults"); + ArrayList result = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + + if (mode == COMMAND_MODE) { + if (isCommandDictate(result)) { + lastCommand = COMMAND_DICTATE; + sayReady(); + } else if (isCommandSelectSentence(result)) { + lastCommand = COMMAND_SELECT_SENTENCE; + selectSentence(); + } else if (isCommandRepeat(result)) { + lastCommand = COMMAND_REPEAT; + repeatLast(); + } else if (isCommandExit(result)) { + lastCommand = COMMAND_EXIT; + sayGoodBye(); + } else { + lastCommand = COMMAND_UNKNOWN; + sayUnknownCommand(); + } + } else { //dictation + lastDictation = result.get(0); + this.mText.setText(result.get(0) + "\n" + this.mText.getText()); + mode = COMMAND_MODE; + lastCommand = COMMAND_NONE; + sayWaitingForCommand(); + } + } + + void sayGoodBye() { + sayMessage(R.string.tts_good_bye); + } + + void sayUnknownCommand() { + sayMessage(R.string.tts_unknown_command); + } + + void selectSentence() { + sayMessage(R.string.tts_sentence_selected); + + Editable editable = mText.getEditableText(); + final BackgroundColorSpan span = new BackgroundColorSpan(Color.YELLOW); + editable.setSpan( + span, + 0, + 10, + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ); + } + + void repeatLast() { + sayString(lastDictation); + } + + boolean isCommandSelectSentence(ArrayList result) { + String command = context.getResources().getString(R.string.tts_select_sentence); + for (String variant : result) { + if (command.toLowerCase().equals(variant.toLowerCase())) { + return true; + } + } + return false; + } + + boolean isCommandRepeat(ArrayList result) { + String command = context.getResources().getString(R.string.tts_repeat); + for (String variant : result) { + if (command.toLowerCase().equals(variant.toLowerCase())) { + return true; + } + } + return false; + } + + boolean isCommandDictate(ArrayList result) { + String command = context.getResources().getString(R.string.tts_dictate); + for (String variant : result) { + if (command.toLowerCase().equals(variant.toLowerCase())) { + return true; + } + } + return false; + } + + boolean isCommandExit(ArrayList result) { + String command = context.getResources().getString(R.string.tts_done); + for (String variant : result) { + if (command.toLowerCase().equals(variant.toLowerCase())) { + return true; + } + } + return false; + } + + @Override + public void onPartialResults(Bundle bundle) { + Log.i(LOG_TAG, "onPartialResults"); + } + + @Override + public void onEvent(int i, Bundle bundle) { + Log.i(LOG_TAG, "onEvent"); + } + + public static String getErrorText(int errorCode) { + String message; + switch (errorCode) { + case SpeechRecognizer.ERROR_AUDIO: + message = "Audio recording error"; + break; + case SpeechRecognizer.ERROR_CLIENT: + message = "Client side error"; + break; + case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS: + message = "Insufficient permissions"; + break; + case SpeechRecognizer.ERROR_NETWORK: + message = "Network error"; + break; + case SpeechRecognizer.ERROR_NETWORK_TIMEOUT: + message = "Network timeout"; + break; + case SpeechRecognizer.ERROR_NO_MATCH: + message = "No match"; + break; + case SpeechRecognizer.ERROR_RECOGNIZER_BUSY: + message = "RecognitionService busy"; + break; + case SpeechRecognizer.ERROR_SERVER: + message = "error from server"; + break; + case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: + message = "No speech input"; + break; + default: + message = "Didn't understand, please try again."; + break; + } + return message; + } + + class VoiceReader { + TextToSpeech tts; + boolean ttsReady = false; + boolean speaking = false; + String text; + String textId; + Locale locale; + + void init(Context context) { + tts = new TextToSpeech(context, new TextToSpeech.OnInitListener() { + @Override + public void onInit(int status) { + // set our locale only if init was success. + if (status == TextToSpeech.SUCCESS) { + ttsReady = true; + speak(text, "word:" + textId, locale); + } + } + }); + tts.setLanguage(locale); + tts.setOnUtteranceProgressListener(new UtteranceProgressListener() { + @Override + public void onStart(String s) { + speaking = true; + } + + @Override + public void onDone(String s) { + speaking = false; + if (mode == COMMAND_MODE) { + if (lastCommand == COMMAND_DICTATE) { + mode = DICTATION_MODE; + startHearingDelay(); + } else if (lastCommand == COMMAND_SELECT_SENTENCE) { + + + } else if (lastCommand == COMMAND_EXIT) { + //exit + } else if (lastCommand == COMMAND_REPEAT) { + lastCommand = COMMAND_NONE; + sayWaitingForCommand(); + } else if (lastCommand == COMMAND_UNKNOWN) { + startHearingDelay(); + } else { + startHearingDelay(); + } + } + } + + @Override + public void onError(String s) { + speaking = false; + } + }); + } + + void startHearingDelay() { + final Handler handler = new Handler(Looper.getMainLooper()); + handler.postDelayed(() -> startHearing(context), 100); + } + + private void speak(String phrase, String id, Locale locale) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return; + this.text = phrase; + this.textId = id; + this.locale = locale; + if (tts == null) { + this.init(context); + return; + } + if (!ttsReady) { + return; + } + + tts.setLanguage(locale); + tts.speak(phrase, TextToSpeech.QUEUE_ADD, null, id); + } + } +} diff --git a/app/src/main/java/com/maxistar/textpad/tts/TextSplitter.java b/app/src/main/java/com/maxistar/textpad/tts/TextSplitter.java new file mode 100644 index 0000000..b69f0cc --- /dev/null +++ b/app/src/main/java/com/maxistar/textpad/tts/TextSplitter.java @@ -0,0 +1,20 @@ +package com.maxistar.textpad.tts; + +import java.io.FileInputStream; +import java.io.IOException; +import java.text.BreakIterator; +import java.util.Locale; + +public class TextSplitter { + void test() { + BreakIterator iterator = BreakIterator.getSentenceInstance(Locale.US); + String source = "This is a test. This is a T.L.A. test. Now with a Dr. in it."; + iterator.setText(source); + int start = iterator.first(); + for (int end = iterator.next(); + end != BreakIterator.DONE; + start = end, end = iterator.next()) { + System.out.println(source.substring(start,end)); + } + } +} diff --git a/app/src/main/res/drawable-anydpi/ic_dictate.xml b/app/src/main/res/drawable-anydpi/ic_dictate.xml new file mode 100644 index 0000000..616070b --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_dictate.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/drawable-anydpi/ic_settings.xml b/app/src/main/res/drawable-anydpi/ic_settings.xml index 3c4f8c8..5350109 100644 --- a/app/src/main/res/drawable-anydpi/ic_settings.xml +++ b/app/src/main/res/drawable-anydpi/ic_settings.xml @@ -1,5 +1,4 @@ - + + - - - - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml index 6644248..43989ed 100644 --- a/app/src/main/res/menu/main_menu.xml +++ b/app/src/main/res/menu/main_menu.xml @@ -95,6 +95,11 @@ android:title="@string/Menu_Print" android:icon="@drawable/ic_print" app:iconTint="@color/colorIcon" /> + مخصص + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8a0bca6..d9a16b5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -100,4 +100,22 @@ Brauch + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected \ No newline at end of file diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4666f3d..0e49f15 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -100,4 +100,22 @@ Costumbre + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 666f50f..819b4a7 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -100,4 +100,22 @@ Costume + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index fe00580..56bf629 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -100,4 +100,22 @@ Personalizada + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0e8a30d..fb56294 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -100,4 +100,22 @@ Пользовательская + Dictate + + ожидаю команду + готов к диктовке + диктовать + готово + прочитать последнее предложение + перейти в начало + перейти в конец + не понимаю + прочитать текущее предложение + продолжай + дальше + пока + неизвестная команда + повтори + выделите предложение + предложение выделено diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 21f5de3..793b1a7 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -101,4 +101,22 @@ Gelenek + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected \ 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 7edab87..7e290b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,4 +104,22 @@ Custom + Dictate + + waiting the command + ready to dictate + dictate + done + read last sentence + go to start + go to end + do not understand + read current sentence + proceed + further + good bye + unknown command + repeat + select sentence + sentence selected diff --git a/app/src/test/java/com/maxistar/textpad/test/nlp/tokenizer/GenericTokenizerTest.java b/app/src/test/java/com/maxistar/textpad/test/nlp/tokenizer/GenericTokenizerTest.java new file mode 100644 index 0000000..47cc851 --- /dev/null +++ b/app/src/test/java/com/maxistar/textpad/test/nlp/tokenizer/GenericTokenizerTest.java @@ -0,0 +1,67 @@ +package com.maxistar.textpad.test.nlp.tokenizer; + +import com.maxistar.textpad.nlp.tokenizer.GenericTokenizer; + +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; + +public class GenericTokenizerTest { + + @Test + public void testTokenizer() { + String inputString = "Однажды, в студеную зимнюю пору\n" + + "Я из лесу вышел; был сильный мороз.\n" + + "Гляжу, поднимается медленно в гору\n" + + "Лошадка, везущая хворосту воз.\n" + + "И шествуя важно, в спокойствии чинном,\n" + + "Лошадку ведет под уздцы мужичок\n" + + "В больших сапогах, в полушубке овчинном,\n" + + "В больших рукавицах… а сам с ноготок!\n" + + "«Здорово парнище!» — «Ступай себе мимо!»\n" + + "— «Уж больно ты грозен, как я погляжу!\n" + + "Откуда дровишки?» — «Из лесу, вестимо;\n" + + "Отец, слышишь, рубит, а я отвожу».\n" + + "(В лесу раздавался топор дровосека.)\n" + + "«А что, у отца-то большая семья?»\n" + + "— «Семья-то большая, да два человека\n" + + "Всего мужиков-то: отец мой да я…»\n" + + "— «Так вот оно что! А как звать тебя?» — «Власом».\n" + + "— «А кой-тебе годик?» — «Шестой миновал…\n" + + "Ну, мертвая!» — крикнул малюточка басом,\n" + + "Рванул под уздцы и быстрей зашагал."; + + String PUNCTUATION_SPACE = " "; + String PUNCTUATION_COMA = ","; + String PUNCTUATION_N = "\n"; + + String[] expectedTokens = new String[] { + "Однажды", PUNCTUATION_COMA, PUNCTUATION_SPACE, "в", PUNCTUATION_SPACE, "студеную", PUNCTUATION_SPACE, "зимнюю", PUNCTUATION_SPACE, "пору", PUNCTUATION_N, + "Я", " ", "из", " ", "лесу", " ", "вышел", ";", " ", "был", " ", "сильный", " ", "мороз", ".", PUNCTUATION_N, + "Гляжу", ",", " ", "поднимается", " ", "медленно", " ", "в", " ", "гору", "\n", + "Лошадка", ",", " ", "везущая", " ", "хворосту", " ", "воз", ".", "\n", + "И", " ", "шествуя", " ", "важно", ",", " ", "в", " ", "спокойствии", " ", "чинном", ",", "\n", + "Лошадку", " ", "ведет", " ", "под", " ", "уздцы", " ", "мужичок", "\n", + "В", " ", "больших", " ", "сапогах", ",", " ", "в", " ", "полушубке", " ", "овчинном", ",", "\n", + "В"," ","больших"," ","рукавицах","…"," ","а"," ","сам"," ","с"," ","ноготок","!","\n", + "«","Здорово"," ","парнище","!","»"," ","—"," ","«","Ступай"," ","себе"," ","мимо","!","»","\n", + "—"," ","«","Уж"," ","больно"," ","ты"," ","грозен",","," ","как"," ","я"," ","погляжу","!","\n", + "Откуда"," ","дровишки","?","»"," ","—"," ","«","Из"," ","лесу",","," ","вестимо",";","\n", + "Отец",","," ","слышишь",","," ","рубит",","," ","а"," ","я"," ","отвожу","»",".","\n", + "(","В"," ","лесу"," ","раздавался"," ","топор"," ","дровосека",".",")","\n", + "«","А"," ","что",","," ","у"," ","отца-то"," ","большая"," ","семья","?","»","\n", + "—"," ","«","Семья-то"," ","большая",","," ","да"," ","два"," ","человека","\n", + "Всего"," ","мужиков-то",":"," ","отец"," ","мой"," ","да"," ","я","…","»","\n", + "—"," ","«","Так"," ","вот"," ","оно"," ","что","!"," ","А"," ","как"," ","звать"," ","тебя","?","»"," ","—"," ","«","Власом","»",".","\n", + "—"," ","«","А"," ","кой-тебе"," ","годик","?","»"," ","—"," ","«","Шестой"," ","миновал","…","\n", + "Ну",","," ","мертвая","!","»"," ","—"," ","крикнул"," ","малюточка"," ","басом",",","\n", + "Рванул"," ","под"," ","уздцы"," ","и"," ","быстрей"," ","зашагал","." + }; + + GenericTokenizer tokenizer = new GenericTokenizer(); + String[] actualTokens = tokenizer.tokenizeString(inputString); + + assertArrayEquals(expectedTokens, actualTokens); + } + +} diff --git a/images/allicons.svg b/images/allicons.svg index 1055987..e5726c9 100644 --- a/images/allicons.svg +++ b/images/allicons.svg @@ -9,7 +9,7 @@ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="allicons.svg" - inkscape:version="1.0 (4035a4fb49, 2020-05-01)" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" id="svg8" version="1.1" viewBox="0 0 210 297" @@ -30,35 +30,22 @@ stdDeviation="0.00078338267" inkscape:collect="always" /> - - - + position="2.9207403,295.28566" + inkscape:locked="true" /> + position="21.524586,292.23793" + inkscape:locked="true" /> + position="10.286085,275.79289" + inkscape:locked="true" /> + position="13.643008,294.39377" + inkscape:locked="true" /> @@ -91,7 +82,7 @@ image/svg+xml - + @@ -102,113 +93,7 @@ inkscape:label="bg"> + d="m 27.317905,-12.461092 h -10 v -4 h 10 m -3,13.9999997 a 3,3 0 0 1 -3,-3 3,3 0 0 1 3,-2.9999999 3,3 0 0 1 3,2.9999999 3,3 0 0 1 -3,3 m 5,-15.9999997 h -12 c -1.11,0 -2,0.9 -2,2 v 13.9999997 a 2,2 0 0 0 2,2.00000005 h 14 a 2,2 0 0 0 2,-2.00000005 V -14.461092 Z" + style="fill:none;stroke:#000000;stroke-width:0.26458299px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + inkscape:connector-curvature="0" /> + + + + + + + + + image/svg+xml + + + + + + + + + +