Размер SMS-сообщения сильно ограничен, что правда, то правда. Ограничен он 160 символами для СМСок состоящих только из латиницы и 70 символами для тех, в тексте которых, содержатся символы национальных языков, например, русского. Причем достаточно всего одной русской буквы в сообщении, что бы его максимальный размер уменьшился более чем вдвое. Давай попробуем разобраться, с чем это связанно, ликвидировать этот ужасный недостаток и вообще немного расширить возможный объем SMS. Кодирование текста
Еще со школы нам известно, что компьютеры и прочие железяки не понимают никаких букв, а представляют всю информацию исключительно в виде набора ноликов и единичек. Соответствие между символами и их цифровым представлением называется кодировкой. В мире существует огромное множество кодировок. Самые распространенные среди них условно можно разделить на три класса: восьмибитные, семибитные, и шестнадцатибитные.
В восьмибитных кодировках для представления одного символа требуется 8 бит или 1 байт, из чего следует, что такой кодировкой можно охватить 256 символов.
В семибитных на кодирование символа отводится уже 7 бит, а количество возможных символов равно 128.
В шестнадцатибитных же кодировках символ кодируется аж 2 байтами, при этом количество охватываемых символов - 65536. Внушительный объем кодировки позволяет вместить в себя символы всех существующих национальных алфавитов одновременно, в отличие от, скажем, восьмибитовых кодировок, которые вмещают только латиницу и еще один национальный алфавит. Представителем шестнадцатибитной кодировки является unicode.
Для кодирования текста SMS используется семибитная кодировка, в случае если в текст состоит только из символов латинского алфавита. А поскольку максимальный объем SMS-сообщения в соответствии со стандартами составляет 1120 бит, то легко подсчитать максимальное количество символов. Оно равно 1120/7 = 160.
Как только в тексте сообщения появляется символ, скажем, кириллицы, кодировка меняется с семибитной на Unicode с ее двумя байтами на символ. Максимальный объем СМСки при этом становится равным 1120/16 = 70.
Unicode как и все универсальное выигрывает в общем случае, но в некоторых частных случаях можно предложить более рациональный алгоритм.
Попробуем придумать свою собственную кодировку, при использовании которой влезало бы больше текста в одно сообщение. Сформулируем требования к ней: кодировка должна позволять использовать символы латиницы и кириллицы, цифры от 0 до 9, а также – символы: ".,!?-_"/:@". Максимальный объем сообщения, представленного нашей кодировкой должен быть не менее 160 символов, а лучше более.
За основу нашей кодировки возьмем шестибитное кодирование. Оно позволяет закодировать 64 символа и в отведенные нам стандартом 1120 бит поместится 186 таких символов.
Разместить в таблице из 64 элементов символы двух алфавитов, знаки препинания и цифры нам не удастся, они просто все не влезут. Поэтому нужно сделать так, чтобы одному коду могло сразу несколько символов, переключение между которым осуществлялось бы с помощью других специальных. Подобная система очень напоминает клавиатуру, где на каждой клавише расположено несколько символов (разного регистра или языка), и выбор осуществляется нажатием клавиши Shift.
Итак, в первых 32 элементах будем хранить символы кириллицы от "а" до "я". Одну букву кириллицы я намеренно исключил. Благо правила русского языка позволяют мне это сделать. Какую? Догадайся сам (подсказка: эта буква ни разу не встречается на страницах этого журнала).
Так же в первых 26 элементах будем хранить символы латиницы от "a" до "z". Т.е. код 2 будет у нас одновременно соответствовать и кириллической букве "б" и латинской букве "b".
Знаки препинания мы поместим с 32 позиции в таблице по 41. Цифры от 0 до 9 самое просто тоже будет поместить в начало нашей таблицы и присвоить им коды от 0 до 9, что, в общем, логично. Теперь коды от 0 до 9 соответствуют 3 символам (кириллица, латиница и цифры). Как же определить какой конкретно символ из трех выбрать? Для этого мы будем использовать символы-модификаторы. В основной таблице у нас осталось 22 пустых ячейки. В часть из них мы и поместим модификаторы.
Нам потребуются следующие модификаторы: переключатель "кириллица/латиница", переключатель "цифры/не цифры", переключатель "заглавные/строчные" и переключатель "следующая заглавная".
Для того, что бы понять, как работают модификаторы, давай закодируем такое сообщение: "Привет Lexa мой НОМЕР - 128. Пока", и посмотрим, что выйдет.
[следующая заглавная]привет [кириллица/латиница][следующая заглавная]lexa[кириллица/латиница] мой [заглавные/строчные]номер - [цифры/не цифры]128.[цифры/не цифры] п[заглавные/строчные]ока
Аналогия с клавишей Shift на лицо. Первое упоминание модификатора - это нажатие клавиши, второе напоминание - это ее отпускание. На начальном этапе модификаторы установлены в положение "строчные кириллические буквы".
Из общего ряда выделяется модификатор "следующая заглавная", он не требует отключения. Преимущество использования его, а не модификатора "строчные/заглавные” очевидно и равно 6 битам.
В Unicode такое сообщение занимало бы 33*2 = 66 байт. В нашей кодировке оно занимает 41*6/8=31 байт. Здорово, сжали сообщение больше чем в два раза, но мы не будем останавливаться на достигнутом. Мы задействовали только 46 кодов и у нас в запасе еще 18. Эти 18 кодов позволяют нам использовать для 3 символов не шестибитное, а четырехбитное кодирование. На рисунке показано за счет чего осуществляется переход к 4 битам, и почему таких символов может быть только 3.
Каким же символам присвоить такие короткие коды? Ответ очевиден: наиболее часто встречающимся в текстах на русском языке – пробелу (о котором, кстати, мы вообще забыли) и буквам "о" и "е".
Проведя несколько экспериментов, легко увидеть, насколько большой выигрыш мы получим за счет использования всего лишь трех четырехбитных кодов.
Для того чтобы осуществить подобное кодирование SMS-сообщений у себя на телефоне потребуется немного разобраться в программировании на J2ME, поддерживающимся на большинстве современных моделей. А для того, чтобы в нем разобраться необходим некоторый софт. Компилятор, среда и эмулятор. Компилятор – сделает из текста программы готовый мидлет, среда нужна для того, чтобы не париться и не изучать разные ключи компилятора, а также, чтобы не искать текстовый редактор. Эмулятор же нужен для тестирования и запуска полученного в результате компиляции приложения.
Смело устанавливай с диска
К сожалению, не все можно протестировать на эмуляторе, особенно если это универсальный эмулятор. Поэтому пользоваться телефоном для тестирования придется. В реальной мобиле некоторые вещи могут работать немного иначе. Мне, например, так и не удалось осуществить отправку SMS с одного эмулятора на другой. Зато отправка сообщения с эмулятор на обычный телефон прошла на ура. Правда, последующие двое суток я получал эту СМСку на свой телефон каждый час. Видимо, на шлюзе, через который происходила отправка, случился сбой :).
Вооружившись всем необходимым для программирования и тестирования софтом, наконец, можно попробовать осуществить нашу идею. Для этого заходим в директорию с WTK и из поддиректории bin запускаем ktoolbar.exe. В появившемся окне выбираем пункт "New Project" и создаем новый проект, sms_test, с таким же названием класса. В следующем окошке не читая, кликаем "ok". На данной стадии настройки нас совершенно не интересуют.
Проект создан и теперь можешь приступать к написанию мидлета. Исходники твоей программки будут храниться в директории apps/sms_test/src. Полный код уже готового мидлета для изучения ты можешь найти на диске, здесь же я рассмотрю только некоторые вопросы.
Итак, цикл разработки приложения для твоей мобилы состоит из двух фаз:
Для того чтобы отправить СМС из своей программы нужно использовать Wireless Messaging API. Набор очень удобных и понятных классов:
Следующий код осуществляет отправку SMS-сообщения c помощью этого API:
try{ String addr="sms://+1234567890"; MessageConnection conn=(MessageConnection) Connector.open(addr); TextMessage msg=(TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE); msg.setPayloadText("Hello World!"); conn.send(msg); } catch(Exception e){}
Как видишь, здесь все очень просто:
В случае с отправкой кодированных по нашему методу сообщений, следует использовать класс BinaryMessage. Соответственно код преобразуется следующим образом:
byte[] bin_msg; try{ String addr="sms://+1234567890:5151"; MessageConnection conn=(MessageConnection) Connector.open(addr); BinaryMessage msg=(BinaryMessage)conn.newMessage(MessageConnection.BINARY_MESSAGE); msg.setPayloadData(bin_msg); conn.send(msg); } catch(Exception e){}
Обрати внимание на то, что в строке адреса появилось дополнение в виде ":5151". Это номер порта, на который следует отправлять СМСку. Дело в том, что существует два вида SMS-сообщений, в которых присутствует номер порта, и в которых его в помине нет. Сообщения первого типа принимаются стандартным ПО телефона. Сообщения второго типа, в свою очередь, должны обрабатываться соответствующими мидлетами настроенными на получение сообщений из данного порта. Если в момент поступления сообщения такой мидлет не запущен, то сообщение передается стандартному ПО мобилы.
К примеру, получать все сообщения пришедшие на 5151 порт можно с помощью следующего простого кода:
try{ String addr="sms://:5151"; MessageConnection conn=(MessageConnection) Connector.open(addr); Message msg=null; while(!ex){ msg= conn.receive(); if(msg instanceof TextMessage){ TextMessage txt_msg=(TextMessage)msg; String text= txt_msg.getPayloadText(); txt_msg.setPayloadText("Received: "+ text); conn.send(txt_msg); } } } catch(Exception e){}
Тут ты можешь столкнуться с небольшой проблемой. Мидлет предназначенный для получения СМСок с определенного порта будет получать их только в то время когда он запущен. В случае если СМСка придет, когда мидлет не запущен, она перенаправится стандартному ПО телефона. Решением данной проблемы является использование технологии Push Registry, которая позволяет установить соответствие между мидлетом и некоторым портом. При поступлении информации в зарегистрированной специальным способом порт твой мидлет автоматически загрузится.
Таким образом, получается некоторое подобие сервера. Для того, что бы связать определенный порт с мидлетом, следует добавить в jad-файл строчку вроде этой:
MIDlet-Push-1: sms://:5151, sms_test, *
Грубо говоря, в этой строке оговорено, что при поступлении сообщений типа sms на 5151 порт, необходимо активизировать мидлет sms_test и передать ему эти данные. Далее мидлет должен проводить декодирование и отображать сообщение в читабельном виде (с этой, достаточно сложной, но очень интересной проблемой предстоит разобраться тебе самому). Посмотреть на уже реализованное приложение, которое принимает, получает, кодирует и декодирует сообщения по описанному выше принципу можно на диске.
К сожалению, технология Push Registry поддерживается только в новых телефонах соответствующих стандарту MIDP версии 2.0. В более старых моделях поддерживающих версию 1.0 реализовать отправку и получение СМСок на порт с которым проассоциирован конкретный мидлет невозможно. А отправка закодированного сообщения на стандартное ПО телефонов не имеет смысла, поскольку текст в этом случае будет нечитаемым.
Кроме того, нигде не оговаривается, как должен активизироваться мидлет при поступлении очередной СМСки на связанный с ним порт. Так, например, на телефонах компании Siemens 65 серии в таких ситуациях просто появляется звездочка в левом нижнем углу экрана, без какого либо проигрывания мелодии или включения виброзвонка. Согласись, это не очень удобно (хотя, наверное, можно самому реализовать сигнал в мидлете).
Еще одной большой проблемой является тот факт, что заявления производителей о соответствии MIDP 2.0 еще ни о чем не говорят. Это очень напоминает ситуацию сложившуюся не так давно на рынке браузеров, когда там царили Internet Explorer и Netscape Navigator. Оба браузера поддерживали JavaScript и HTML, но оба делали это по-разному и имели разную объектную модель, в связи с чем разработчику приходилось делать фактически разные версии сайтов для разных браузеров.
Что ж, остается только надеяться, что в скором времени этой неразберихе придет конец и мы сможем, наконец-то, писать универсальные мидлеты работающие на всех телефонах независимо от производителя.
Метод numberOfSegments класса MessageConnection позволяет узнать, на сколько сегментов будет разбито сообщение во время отправки.
Исходник примера:sms_test.rar
Источник:Хакер №075,.
Автор: Филипп Коряка.