[Из песочницы] Математический пакет для Android своими руками
Поводом к написанию этого поста послужила статья «Mathcad Express — бесплатный математический редактор, про который мало кто знает».Мне понравилась дискуссия, развернувшаяся по поводу этой статьи. Однако мне более интересен немного иной аспект дискутируемой проблемы, а именно: нужна ли система компьютерной алгебры на смартфоне или планшете, или это такой странный таракан в голове околоайтишного инженера?
Постановка проблемыКак известно, тяжела и неказиста жизнь простого программиста. Около года назад, когда я осознал вычислительные возможности своего китайфона с четырьмя ядрами на платформе Android, захотелось тряхнуть своей научной стариной и что-нибудь посчитать на телефоне. Play-маркет может все, подумалось тогда.Каких-то особых ограничений не было, за свою научную карьеру я успел попользовать и Mathcad с MATLAB, и Математику, и FEM-системы (Ansys). Поэтому для начала стал смотреть все подряд.
Сложилась такая картина (я не буду приводить ссылки, желающие легко могут найти это на Play-маркете):
Matlab Mobile, как расширение лицензионной копии десктопного варианта. У меня таковой дома не оказалось, поэтому этот вариант, к сожалению, отпал; Сервис Wolfram Alpha и туча обучалок от Wolfram Group — не совсем то, что у меня ассоциируется со словами «посчитать самостоятельно»; Клоны «Матлаба» и «Октавы» под Android — уже теплей, но писать код на экране в 5 дюймов не очень-то удобно; Огромное количество научных калькуляторов разной степени навороченности. Их всех объединяет одно — ввод и представление результатов организованы как в классическом (хардверном) графическом калькуляторе. Основной минус такой идеи — это отсутствие «документа», который можно сохранять, изменять и пересчитывать вновь и вновь; Ничего, что походило бы на Маткад, тогда найти не удалось. Почему я здесь и в начале поста упомянул про Mathcad? На мой взгляд, эта идея хорошо подходит под задачи мобильной математики:
На небольшом экране устройства с пальце-ориентированным интерфейсом все-же удобней работать с формулой, если она записана в естественном математическом виде, а не в виде кода на скриптовом языке; Мобильное устройство хорошо подходит для того, чтобы что-то быстро попробовать. Поэтому от мобильной математической системы требуется удобный интерфейс, но не требуется серьезных вычислительных возможностей; В идеале, в такой системе нужна функция «Я пиарюсь». То есть не только быстро записать сложную формулу и построить пару графиков, но и удобно опубликовать это во всех своих социальных сетях. Формулы в естественной математической нотации здесь тоже более удобны, чем скрипт. То есть я бы с удовольствием установил бы Mathcad mobile на свой смартфон. Но вот его-то (в смысле «Маткада», а не смартфона) на play-маркете и не оказалось. Отсюда и родилась идея разработать нечто подобное самостоятельно.
Формулировка задачи Итак, я решил написать с нуля Андроид-приложение, предназначенное для создания и работы с математическими вычисляемыми документами.Требования к приложению я сформулировал так:
Максимально широкий охват поддерживаемых устройств. Отсюда минимальный API level 8; Поддержка экранов различных разрешений и ландшафтной/портретной ориентации; Формат хранения документов — свой на основе XML; Подробная документация. Чтобы не плодить сущностей, для документирования используется тот же самый формат, в котором хранятся сами документы; Для создания печатной документации предусмотреть экспорт в LaTeX, так как он удобен для записи формул и дает возможность сгенерировать PDF; Математика безгранична. Все не реализовать. Поэтому начальный функционал включает в себя функции многих переменных, графики (2D и 3D), численное интегрирование и дифференцирование, логический оператор. Далее в зависимости от хотелок пользователей; Возможность вставки текста и изображений, но функционал редактирования текста для начала минимальный; Удобность использования и качество имеют приоритет над функционалом; Языки только те, что знаю сам и смогу развивать и поддерживать без обращения к переводчику. И, наверное, самое главное. Это — хобби. Поэтому важно оставаться реалистом и не замахиваться на то, что невозможно реализовать одному человеку за разумное время в режиме 5–6 дней в неделю по 2 часа.
Так как проект получил статус «любимое хобби на ближайший год», то отсюда два важных следствия:
Я решил не ограничивать себя в разумных тратах на технику/продвижение/услуги, если таковые потребуются; Такие цели, как «Заработать любой ценой» и «Оказаться в топе» не ставятся, поэтому я решил не добавлять рекламу в приложения и отказаться от продвижения через мотивированные загрузки. В то же время, я противник полной халявы. Поэтому выбрал следующую модель монетизации:
Само приложение является платным и распространяется только через Play-маркет; Имеется бесплатная версия с идентичным внешним видом и интерфейсом, но с урезанной математикой. Причем этот функционал не деактиварован, а отсутствует на уровне кода. То есть бесплатную версию физически нельзя переключить в полный режим и она распространяется везде, где только можно. Такая модель легко реализуется с использованием системы контроля версий (в моем случае SVN), где основная ветка предназначена для экспериментов и разработки, а две дочерние — для релизов платной и бесплатной версий соответственно.
На этом можно подвести черту под вводными данными и приступить к обсуждению реализации.
Метод решения На мой взгляд, самый главный вопрос такой — как организовать на смартфоне ввод и редактирование формул, которые можно вычислять (для начала только численно), но при этом они представлены в естественном математическом виде? Как скомпоновать из этих формул, графиков, текста и загружаемых из файла картинок единый документ, способный самовычисляться? Многие из вас наверняка использовали вордовский редактор формул. На большом экране, да с клавиатурой и мышкой под рукой все очень просто. На экране видны различные математические палитры (или легко доступны из меню), мышкой позиционируется курсор, а далее либо с клавиатуры, либо мышкой из палитры вводится нужный символ. Часть формулы можно легко выделить мышкой, скопировать, заменить или перетащить в другое место. Сами формулы как плавающие объекты можно перетащить мышкой в любую часть документа. Знакомо, да?
А теперь то же самое на сенсорном экране, без мышки, без хардверной клавиатуры, где виртуальная клавиатура перекрывает треть (в ландшафтной ориентации половину) экрана?
Начну с компоновки объектов в документе. Вариантов, на самом деле, не очень много:
Каждый объект имеет свои координаты и объекты могут располагаться относительно друг друга произвольно, как в векторном графическом редакторе. Именно этот вариант реализован в самом Маткаде. Под Андроидом можно использовать для этого устаревший AbsoluteLayout, но при этом необходимо реализовать выравнивание/распределение объектов и групп объектов. То есть в этом варианте нужен дополнительный пользовательский интерфейс, не относящийся к основной задаче. Для настольного компьютера это не критично, однако юзабилити мобильного приложения, на мой взгляд, от этого будет хуже; Расположение в ячейках таблицы, как в MS Excel. Этот вариант я отмел сразу, так как математический документ — это все что угодно, но только не таблица; Простой список, где каждый объект в своей строке. Именно так устроены большинство научных калькуляторов. Именно это я и реализовал в первой версии приложения. Дешево и сердито. На что получил вполне резонные замечания пользователей, что такой метод очень неудобен, так как есть объективная потребность группировать некоторые формулы по строкам; В результате получилась упрощенная версия RelativeLayout, этакий двумерный список, где по умолчанию объекты добавляются вертикально (снизу выделенного объекта), но с помощью одного единственного окна можно добавить объект как справа, так и слева от выделенного:
Пока я отказался от режима захвата и перетаскивания объектов при движении пальца по экрану, так как такой интерфейс в случае двумерного списка требует некоторой проработки. Вместо этого реализована возможность группового выделения объектов и групповые операции через контекстное меню: удаление, копирование в буфер и замена выделенного объекта содержимым из буфера.
Следующий вопрос связан со вводом информации. Многие калькуляторы имеют для этого свою виртуальную математическую клавиатуру. Мне же этот подход не кажется оптимальным. К примеру, я сам использую планшет с внешней USB клавиатурой, и в этом случае он имеет ландшафтную ориентацию. Выкатывать в такой ситуации виртуальную клавиатуру, которая занимает половину экрана, не совсем логично. Поэтому я изначально нацелился на то, что приложение будет работать со стандартной системной клавиатурой и не требовать специальных клавиатурных расширений. То есть абсолютно все математические символы можно ввести в виде кода с клавиатуры, и эта идея опять же позаимствована из Маткада. Для ввода греческих символов я просто добавил греческий язык к клавиатурным раскладкам.
Однако вынуждать пользователя запоминать все коды — не есть хорошая идея. Поэтому внизу экрана, вне зависимости от ориентации, расположена вторая панель инструментов, по оформлению и размерам идентичная верхней основной. Все математические символы, коих сейчас около 50-ти, расположены в одну линию на этой панели, а сама она прокручивается вправо и влево. При коротком нажатии символ вводится. При долгом нажатии на символ всплывает подсказка, что это и какой код ему соответствует:
В подтверждение этой идеи хочу процитировать отзыв одного из пользователей: «Beeing able to use shortcuts instead of symbolic buttons (but not beeing forced to do so) makes the app perfect».
А куда вводятся коды или символы? Здесь опять все похоже на Маткад. При добавлении объекта появляются пустые поля ввода. В них можно вводить текст, числа или символы. При вводе, например, символа деления вместо поля ввода появляется дробь с двумя полями, в одно из которых перекочует текст, введенный ранее в удаленном поле. Это основной режим ввода, который хорош, когда точно знаешь последовательность набора нужной формулы:
А если не знаешь или нужно что-то изменить в формуле? Контекстное меню в помощь. Оно активируется при долгом нажатии на часть формулы и предоставляет доступ к буферу обмена. Кроме этого, там есть кнопка расширения области выделения. С нижней панели инструментов можно ввести символ, который будет применен к выделенному блоку:
Получилась своего рода кооперация. Пользователь, вводя формулу таким методом, сам же и выполняет основную работу по ее синтаксическому разбору. А приложение в благодарность за это формулу просчитает.
Таким образом, каждая формула имеет два аспекта:
Во-первых, это иерархия вложенных лайаутов, которые содержат как поля ввода, так и математические обозначения. Визуально это обычная формула.
Во-вторых, это иерархическая структура примитивных математических токенов, каждый из которых умеет вычисляться. Каждый токен связан с соответствующим ему лайаутом и является его владельцем. Подводные камни И вот здесь меня поджидала серьезная засада. Де-факто в Андроиде есть ограничение на количество вложенных лайаутов, связанное с размером стека вызова процедур. Компилятор в этом случае ничего не скажет, но во время работы (в методе android.view.View.draw одного из вложенных лайаутов) приложение валится с исключением типа StackOverflowErrors. Подробнее об этом здесь.Отловить это исключение реально, так как у меня имеется собственная реализация метода onDraw () в самом начале этой цепочки вызовов, но при этом, начиная с определенной сложности формулы, она просто перестанет прорисовываться. Пришлось вручную контролировать глубину вложений элементов формулы, и, начиная с некоторой критической глубины (подобранной экспериментально), выдавать несимпатичное, но все-же необходимое сообщение «К сожалению, достигнут предел глубины формулы для данной версии Андроида».
Экспериментально я подобрал следующие значения предельной глубины, считая от корневого RelativeLayout фрагмента:
если API level < 15 (Андроид 2.2.x — 4.0.x), то максимальная глубина составляет 7 вложенных уровней лайоутов; если API level от 15 до 17 (4.1.x — 4.2.x), то 9 уровней; если API level > 17 (4.3.x и выше), то 15 уровней. Вторая проблема — отсутствие стандартной компоненты, которая бы сочетала в себе горизонтально-вертикальный скроллинг и масштабирование. Эти действия вроде как поддерживаются WebView, но вот сама WebView мне никак не подходит. Решение топорное, но работает — я взял исходники стандартных ScrollView и HorizontalScrollView, объединил их в одну компоненту и прикрутил детектирование и обработку изменения масштаба. Что я при этом так и не смог довести до ума — это корректное позиционирование в документе после изменения масштаба.
Документация и локализация Читая отзывы пользователей на Play-маркете про различные научные калькуляторы, я обнаружил, что одним из слабых мест большинства калькуляторов является отсутствие документации. Иногда полное. Поэтому я решил уделить документации достаточно много внимания. И добавил ее как в само приложение (для этой цели удобным оказался NavigationDraver), так и выложил в сети в виде PDF и добавил в приложение ссылки для прямой загрузки.
В приложении разделы документации хранятся в XML и открываются как отдельные фреймы. Имеется функция конвертации документа в LaTeX. Утилита pdfletx на рабочем месте довершает дело. Результат не стыдно выставить на всеобщее обозрение.
Теперь пару слов про локализацию. Разработка по старой привычке ведется на английском. Когда программа откомпилировалась и запустилась (то есть готова к продаже), интерфейс и документация вновь добавленной фишки переводятся на два других языка, один из которых русский.
И вот тут я был приятно удивлен статистикой загрузок в консоли разработчика. Математика в Рунете в почете! К слову сказать, русскую версию я выпустил не сразу, а когда количество загрузок приблизилось к тысяче. Но это практически не увеличило и без того высокий процент загрузок из России и стран ближнего зарубежья. На текущий момент распределение загрузок для бесплатной версии по языкам и странам выглядит так:
То есть Россия, Украина и Казахстан вместе обеспечивают более 40% загрузок. Платная версия дает другую картину: загрузки из России доминируют лишь с незначительным отрывом.
Заключение Ну что же, написано уже много, пора закругляться. С вычислительной точки зрения очень многие нужные вещи еще не реализованы: нет комплексных чисел, нет поддержки массивов, нет даже примитивной матричной арифметики, нет решения уравнений. Но тут возникает резонный вопрос —, а надо ли это на телефоне? Интересно услышать мнение уважаемой аудитории.Что касается ближайших задач, то пока основные пожелания пользователей касаются наращивания экспортных возможностей. К примеру, один из последних комментариев: «Интересует такой вопрос, есть хоть какая нибудь возможность связать документ с маткадом? Что бы была возможность доработать документ на ПК». По поводу Маткада лично я сомневаюсь, так как формат проприетарный, но такие вещи, как экспорт в HTML (буквально сегодня обнаружил фреймворк MathJax), в PDF, или какой-нибудь открытый офисный формат можно реализовать.
На этом хочу хочу поблагодарить за внимание всех, кто дочитал до конца, и с чувством выполненного долга откланяться. Буду рад вопросам, если таковые появятся.