воскресенье, 15 декабря 2013 г.

Конкурс Осенняя Мобилизация

Конкурс Осеняя Мобилизация

Embarcadero предлагает приятный повод поближе познакомиться со своими новыми технологиями в Delphi. Широко рекламируемые FireMonkey, FireDAC, Android и iOS - всё, что хотелось попробовать, но до чего всё никак не доходили мои усталые натруженные руки. Овладевание тем инструментом, который есть, но лежит ещё без дела - не единственный плюс от участия в конкурсе. Есть ещё, конечно, слабая надежда на выигрыш. А с надеждой жить гораздо интереснее. И ещё есть надежда, что читая блоги участников, жюри сделает выводы о том, как лучше помогать программистам в разработке, как улучшить Delphi. Ну и, как знать, возможно удастся подсмотреть что-нибудь полезное у других участников. Обмен, так сказать, опытом.

Мной на конкурс представлено приложение WhoIsWho - аналог нашего корпоративного телефонного справочника. Слепленное на пробу из примеров приложение вдруг стало популярным в нашем коллективе. Люди гораздо охотнее пользуются приложением, чем смотрят те же данные на корпоративном сайтеТакое вот открытие.

https://subversion.assembla.com/svn/who-is-who/trunk/WhoIsWho

Описание назначения приложения:

WhoIsWho - Программа предназначена для знакомства сотрудников в организации друг с другом. И ещё хотелось бы обратить внимание общественности на DeployFolder - сопутствующая программа, которую, возможно, также следует считать кандидатом на участие в конкурсе.

Приложение содержит данные телефонного справочника с корпоративного портала - фотографии, имена, телефоны, дополнительную информацию. Пользователь может отсортировать и отфильтровать данные различным образом, использовать данные приложения в других программах, делать заметки. После установки приложения сетевые соединения не требуются. Обновление данных осуществляется переустановкой приложения.  Интерфейс имеет различные представления для вертикальной и горизонтальной ориентации смартфона, а также отдельную форму для планшетов и дисплея ПК.

В исходный код включены следующие проекты:
1.WhoIsWho - Заявленное на конкурс приложение для Android, iOs, Windows.
2.GetWIWDate - Windows-приложение для получения данных из корпоративной базы данных для приложения WhoIsWho. Сначала я просто сохранял таблицу из MS Management Studio через MS Excel, но потом решился на FireDAC. FireDAC - страшное слово, куча страшных компонентов. Но на удивление, пользоваться оказалось не страшно, и даже можно сказать - удобно. Только исходники FireDAC в поставке с Delphi - явно устаревшие.
3.mess - Windows-приложение для подготовки данных к конкурсу - превращение реальных данных в случайные. Там есть пример смены символов в строке.
4.DeployFolder - Windows-приложение для формирования части deployment Delphi-проекта. Не смотря на то, что это не android, это важный инструмент для разработки на android.

Основной сценарий работы:

1. Упомянутое в разговоре имя сотрудника можно использовать в строке запроса, чтобы понять, о ком шла речь.
2. Увиденного случайно человека можно разыскать по фотографии, применяя в поиске гендерные и возрастные признаки.
3. Легко разыскать нужного сотрудника, использовать номера телефона и прочие сведения, включая фото, для звонка, отправки почты или формирования контактной записи в телефоне и т.п., независимо от доступа к корпоративной сети.
4. В корпоративной среде бывает очень полезно посмотреть, кто родился в текущем или следующем месяце.

А теперь надо рассказать о создании программы, проблемах, с которыми пришлось столкнуться и решениях, выраженных в исходном коде.

Идея и реализация.
За основу были взяты примеры TabletMasterDetailWithSearch и шаблон нового приложения Phone-Master Detail.  Славный Ярослав Бровин показывал, как делать формы для разного формата экрана для iOS, но пример для Андроида  - PhotoEditorDemo я увидел только спустя некоторое время. Поэтому я не стал наследовать формы. Может быть, оно и к лучшему.

Хранение данных - трудности выбора
Как и FM, FireDAC был мне чужд. Но как быть? Предлагался IBlite, были примеры с SQLite. И главная непонятность - биндинг! IB напугал лицензированием и разнообразием форм. Не возникло к IB доверия и из-за неудачного примера в поставке к Delphi. SQLite тоже вызвал вопросы. Хотя в поставке для Windows не было нужной DLL, она легко нашлась в Сети. А с русскими буквами - тоже проблема. Опять-таки в поставку вошёл неудачный пример для SQLite. Позднее я увидел, что FireDAC() спасает. Но FireDAC - это ещё одна terra incognita. Слишком много неизвестного для начала - FMX, binding, new DB engine. Совершенно было непонятно, как перейти от источника примерных данных к источнику данных реальных.

Хранение данных - простое решение
Добрый Всеволод Леонов в своём блоге как-то писал о простом текстовом IO для мобильных устройств. Я попробовал - да. Так что - в топку binding, здравствуйте мои дорогие TStringList и INI! Осталось только положить рядом фотографии - и что ещё нужно?

Проблема множества файлов
Deployment сделан ужасно. Мало того, что он сортировку сбрасывает, глючит - задваивает и вдруг перестаёт видеть файлы, так им ещё и просто невозможно добавить количество файлов, превышающее пару десятков. Попробуйте добавить для всех конфигураций путь на устройстве - это каторга.  Я разговаривал на конференциях с людьми - как и что делать, никто не знал. Тогда я сделал для Deployment специальный инструмент DeployFolder. Теперь стало легко добавлять тысячу файлов. И даже когда добавлен только один файл - гораздо удобнее запустить DeployFolder, чем писать пути руками.

Проблема именования пути
Вот это была засада! Пока у меня не было исходников (в триале), выяснить причину беды было невозможно. На симуляторе iOS - всё прекрасно. А на Androide - даже не запускается. Оказывается, дело в искажении имён каталогов при деплойменте. Сами файлы, размещаемые в подкаталогах, остаются в том регистре, в котором они и были. И имя папки - тоже в том же регистре. Но! - в списке файлов для распаковки на устройстве каталоги - в нижнем регистре! И при этом, когда приложение стартует и выделяет из себя данные, в System.StartUpCopy.CopyAssetToFile даже нет никакого assert, на доступность упакованных в приложение файлов.Там написано "// We have a valid AssetManager. Start", но нет "// We have a valid Asset File". Так что вот.

Проблема с добавлением своего стиля
Как добавить новую стильную картинку на компонент, чтобы на разных платформах отражалось по-разному? Я не знаю. Евгений Крюков рассказывал, что не сложно - стоит нажать правую кнопку, и... И вот нет на мобильной форме команды редактирования стиля. А на немобильной - нет ни правой части редактора, где отображена структура разметки, ни содержимого центральной. Редактор стилей не работает.

Что использовать вместо пиктограммы
В Windows вообще нет пиктограмм. Я использовал вместо пиктограммы текст. Мне так понравилось использовать символы, что я так и оставил в телефонной форме букву @ вместо стиля actiontoolbuttonbordered. А как взять из стиля пиктограмму для некнопочного контрола? Просто так картинку нельзя, она должна быть в общем стиле, меняться от платформы к платформе. Я захотел сделать свой сплиттер-експандер, чтобы увеличивать и уменьшать область фотографии. Мне понравились значки "вверх" и "вниз", но они разные на разных платформах. Пришлось взять кнопки целиком. Но они всё время хотели съехать вниз. Чтобы отцентровать их по вертикали я использовал дополнительные Layout. Что получилось - можно увидеть на вертикальной телефонной форме.

Интерактивные жесты 
Интерактивные жесты лучше кликов и стандартных жестов дают обратную реакцию - не надо ждать завершения касания. Но в этом и трудность - они непрерывны, и когда реакция обработана, пользователь ещё не закончил движение, и мы снова ловим жест, который уже не нужен. Например, двойное нажатие на фото и сдвиг по нему - если двигать часто, то это может сойти за двойной тап. Не забываем про Handled! А чтобы "прекратить" отслеживать интерактивный сдвиг я придумал смотреть на время. Если с последнего зафиксированного и обработанного сдвига прошло меньше четверти секунды, то это ещё остаток обработанного движения, и на него не стоит реагировать.

Права на внешнее хранение
Я не знаю, как обновить приложение, не убив его внутренние данные. К счастью, в Android есть внешние каталоги. Но! - если использовать что-то вроде TPath.GetSharedDocumentsPath, то можно нарваться на пустую строку! Шайтан спрятался в пермишинах проекта - надо посмотреть список прав и поставить нужные external write галочки.

Каталог устройства, легко доступный с ПК
Я посмотрел, что корни файловой системы на разных Android-устройствах разные. Но есть у них общее - там лежит папка /Android/. Вроде, все так и делают - берут SharedDocumentsPath и оставляют от него только корень, чтобы сделать свою папку с понятным названием. Но об этом в учебниках не сказано.

OnChange
Событие OnChange в FMX ведёт себя совсем не так, как в VCL. Раньше присвоение ItemIndex или IsChecked не вело к вызову события. А теперь - да. Поэтому при инициализации формы я использую специальный флаг - IsCreated, чтобы отличить внутренние присвоения от действий пользователя.

TPopup
Когда я запускаю TPopup, он выскакивает там, где мышь или было нажатие. Хотелось бы управлять позицией TPopup, но не знаю как. Не сразу я поборол прозрачность TPopup - оказалось, что надо не забывать установить StyleLookup. Ещё при старте следует ограничивать размер TPopup - иначе, он вылезет за границу экрана.

TComboBox vs TPopupBox
TComboBox и TPopupBox выглядят на мобильных устройствах одинаково:  "барабан" на iOS и диалог с радиокнопками на Android. Явное различие появляется на Windows: TComboBox делает прокручиваемый список, а TPopupBox рисует панель, которая может вылезти далеко за пределы экрана.

Хардварный Бэк
Пока не нашёл, как по кнопке "Назад" в Андроиде программно прятать приложение. Когда ходим по закладкам и возвращаемся, то запросто нажать кнопку лишний раз. Если спросить пользователя о закрытии, то что бы он ни ответил, программа не закроется. Пока прибиваю Halt(0). Но это - конечно, варварство.

Флушинг конфига
Я пишу в INI-файл конфигурацию сразу, как что-то поменялось. Но если программу взять в список задач и выкинуть оттуда, то она закроется, не успев сказать "ой", и изменения не будут записаны. Поэтому сразу после Write<T> нужно делать UpdateFile.

Unicode - Проблема со шрифтами 
Википедия даёт нам знаки зодиака в виде юникодных кодов. В Windows эти знаки прекрасно отображаются. Казалось бы, Unicode должен работать на всех платформах, но увы. Пришлось отрезать значки на мобильных устройствах.

Unicode - Особенности текста 
Во-первых, нельзя забывать об указании кодировки при работе с TStringList. Иначе, по умолчанию под Windows будет сохранятся Ansi, который не стоит перетаскивать на мобильное устройство.  Ещё одной особенностью является необходимость указания разделителя строк #13#10 для iOS. Иначе при разборе текста строка может перенестись "вдруг" где-нибудь в середине.

Локализация
Все диалоги имеют кнопки на английском, хотя я указал, что приложение имеет русский язык в качестве предпочтения. Либо FMX не понимает этого, либо я не понимаю, как ей это сказать

Проблемы iOS - Delphi
Конкурс проводится, конечно, для Android. Но суть мобильной разработки на Delphi - многоплатформенность. Поэтому нужно сказать, что у меня с iOS большие проблемы. Во-первых, я не могу запустить отладчик с реальным устройством - Delphi ругается, что у меня два одинаковых сертификата - в системной и в пользовательской связках ключей. Во-вторых, после Ad Hok Delphi виснет с указанием последнего файла в списке Deployment. Delphi я снимаю диспетчером, а файл на Маке получается рабочий.

Проблемы iOS - FMX
Я использую TPopup для ввода параметров. Если для закрытия используется кнопка - не важно, TButton или TSpeedButton, то всё падает. Убрал для iOS кнопки - приходится тыкать в свободное место, что мне кажется неудобным. Кроме того, событие OnClose срабатывает дважды! Ну и зашарить картинку - никак: едва показывается какая-то панель со значками, и всё вылетает. Уж iOs, казалось бы, не первый год осваивается FMX. Вот же ж.

Проблемы iOS - Apple
Не придумал, как сохранить файлы конфигурации, чтобы при обновлении программы настройки сохранялись. Особенно актуально это стало, когда в программе появились пользовательские заметки.

Досадные мелочи при разработке
Мне досаждает порча dpr при добавлении юнита в проект. Delphi добавляет кучу строк  Application.CreateForm(TPhoneMasterDetail, PhoneMasterDetail) и убирает , Androidapi.JNI.GraphicsContentViewText. Я захожу в историю (спасибо челу, который сделал эту штуку в Delphi!) и копирую Androidapi.JNI.GraphicsContentViewText оттуда. Возможно, следует вынести переключение формы в отдельный модуль, но как-то лениво. 

Ограничения смены ориентации
Не понимаю, зачем в FMX сознательно исключается из ориентации телефонного форм-фактора "обратно-вертикальное" положение. Типа, "кнопки" вверху на телефоне не должны быть. С чего бы это? Мало ли, как у телефона провода или ещё какие боковые элементы управления расположены! Иногда перевернуть телефон - очень нормально. Например, Yandex Navigator вертится всяко. А некоторые приложения - не хотят. Но пусть это нежелание лежит на совести программиста. Нельзя лишать программиста возможности сделать "неправильно", но так, как удобно пользователю.

Нехватка мощностей
Люди жалуются, что некоторые фото на Андроиде - прямоугольник Малевича, т.е. чернота. Я посмотрел - да, начиная примерно с около 400 КБ фото чернеют. Поэтому при загрузке данных я добавил в GetWIWDate пробежку по картинкам, где уменьшаю на 3/4 габариты фото, которые больше 375 КБ. Если фото не уменьшилось до нужного размера, процесс повторяется. Интересно, что от владельцев iOS жалоб не поступало.

Прочие мелочи-находки
Не сразу нашёл, поэтому, наверно, кому-то, новичку интересно будет. По умолчанию FMX делает форму для Windows на весь полный экран без заголовка и панели задач. Эта неприятность исправляется в Object Inspector - Full Screen. Убрать "макет устройства" - Alt+F12 и DesignerMobile = False. Ф-я IsTablet присутствует в платформенных модулях графики.  Я за образец взял PhotoEditorDemo - пример, где Ярослав сложил эти ф-ии вместе. Это скорее всего просто косяк FM, но после того, как я изменил содержимое memo (даже внутри BeginUpdate-EndUpdate) в Windows надо вызывать Repaint - иначе изменений не видно, пока не кликнешь по контролу мышкой. Ещё удивился, что (см.GetWIWDate ) между TBitmap.CreateFromFile и TBitmap.SaveToFile надо вставлять TFile.Delete - иначе при записи файл будет занят!

А ещё - мне ОЧЕНЬ-ОЧЕНЬ-ОЧЕНЬ не хватает WITH. Любил я его.


9 комментариев:

  1. >А ещё - мне ОЧЕНЬ-ОЧЕНЬ-ОЧЕНЬ не хватает WITH. Любил я его.

    Так оно никуда не делось. Возможно, пока, но оно на месте.

    ОтветитьУдалить
    Ответы
    1. Этот комментарий был удален автором.

      Удалить
    2. WITH - штука не необходимая, но полезная, по моему мнению.
      Однако, абсолютно твёрдо заявлено было, что всё - больше его не будет, на мобильных платформах учитесь жить без него. Вот и учимся. Строки, например, уже точно нельзя побуквенно менять. Сейчас все мобильные приложения я пишу без WITH.
      Или это всё только разговоры, а я, как глупый человек, всему верю и живу, как мне говорят?
      В любом случае, Алексей, спасибо за важное замечание.

      Удалить
    3. На счет with согласен, очень удобная штука. Возможно потом её выкинут, но пока даже посимвольная модификация строк работает :). Однако, в будущем, вероятно, работать не будет, как и with. В общем, лучше не надеяться :)

      Удалить
    4. Алексей, знаете, вы меня опять удивили, т.к. я был просто уверен в реализованных "новшествах". Помню, ещё когда обсуждали c Ярославом Бровиным XE3,5 (такая "промежуточная версия" для внутреннего пользования была), Ярослав мне заметил, что WITH для iOS уже не компилится. Мне казалось, что в сентябре я пробовал и строки, и with, и расстраивался в виду невозможности использования старых приёмов. Что это со мной - замещение памяти? Промывка мозгов пропагандой? Или после апдэйта всё вернулось? Может, скоро ANSI (или Utf8) возвратятся? Ещё раз спасибо за замечание.

      Удалить
    5. На счет однобайтовых строк тоже не все однозначно. Никуда их не выкинули, просто немного припрятали: http://andy.jgknet.de/blog/2013/10/the-return-of-the-byte-strings/ и потом http://andy.jgknet.de/blog/2013/12/system-bytestrings-support-for-xe5-update-2/ Но на это, опять же, рассчитывать не стоит. Хотя, кто знает. Запрос на возвращение однобайтовых строк в QC (http://qc.embarcadero.com/wc/qcmain.aspx?d=119501) является самым "голосуемым". Может и вернут, когда-нибудь.

      Удалить
  2. перевыложите пожалуйста исходники!

    ОтветитьУдалить