Мобильное приложение для оценки эффективности мерчендайзинга торговой компании

  • Вид работы:
    Дипломная (ВКР)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    1,04 Мб
  • Опубликовано:
    2014-08-26
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Мобильное приложение для оценки эффективности мерчендайзинга торговой компании

Содержание

Введение

1. Обзор литературы

2. Системное проектирование

3. Функциональное проектирование

3.1 Описание взаимодействия с сервером

3.2 Описание классов системных компонентов

3.3 Описание классов взаимодействия с данными

3.4 Описание классов пакета dialogs

3.5 Обзор функциональных классов из пакета helpers

3.6 Описание классов пакета networking

4. Разработка программных модулей

5. Программа и методика испытаний

6. Руководство пользователя

7. Технико-экономическое обоснование проекта

7.1 Описание проекта

7.2 Расчет сметы затрат и цены ПО

8. Обеспечение пожарной безопасности на ЗАО «Итранзишэн»

Заключение

Список литературы

Приложения

Введение

Данный дипломный проект является частью системы, которая используется маркетинговым отделом компании для мониторинга и анализа работы сотрудников (мерчендайзеров), а также для слежения за динамикой продаж в соответствии с проделанной работой.

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

Разработка системы преследует две цели: с одной стороны, облегчить рядовым сотрудникам удобство и простоту ведения отчётности о проделанной работе, а с другой - предоставить руководителям возможность контроля за работой подчинённых.

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

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

Проект предусматривает использование в закрытой инфраструктуре, поэтому сложно сказать, существуют ли схожие решения. Хоть это и не афишируется, вероятно, подобные системы используются другими компаниями, однако, скорее всего, имеют свои особенности, связанные с производственной отраслью и предметом наблюдений.

1. Обзор литературы

В основе программирования для операционной системы (ОС) Android лежит программирования на Java. Несмотря на свою закостенелость и ощутимое неудобство в работе, Java является одним из самым популярных языков программирования. Язык имеет большую историю - первый релиз Java состоялся ещё в 1995 году - поэтому не удивительно, что существует огромное количество учебников и специализированных сайтов, посвящённых Java-разработке. В качестве ярких примеров можно привести наиболее распространённые [1] и [2].- объектно-ориентированный язык программирования, разработанный компанией Sun Microsystems (в последующем приобретённой компанией Oracle). Приложения Java обычно транслируется в специальный байт-код, поэтому они могут работать на любой виртуальной Java-машине вне зависимости от компьютерной архитектуры. Bиртуальная машина Java (Java Virtual Machine, далее JVM)) - основная часть исполняющей системы Java, так называемой Java Runtime Environment (JRE). Виртуальная машина Java интерпретирует байт-код, предварительно созданный из исходного текста программы компилятором.

В своё время Java удалось сдать стандартом де-факто для бизнес решений. В многом на это повлияли удобство программирования (на тот момент это ещё было актуально) и простота освоения языка: синтаксис языка Java представляет собой более простой вариант синтаксиса языка С++. Зная С++, несложно перейти к языку Java. В настоящий момент Java ощутимо проигрывает в удобстве использования большинству современных языков программирования, однако не спешит сдавать позиции в рейтинге популярности. В основном это обусловлено политикой компании Oracle, из-за которой темпы развития Java существенно отстают от других распространённых языков программирования. Java-сообщество по-прежнему обширно, а экосистема языка содержит гигантскую кодовую базу с тысячами сторонних библиотек, существенно расширяющих функциональность. Несмотря на первый взгляд, оптимистичную картину, без многих популярных библиотек функциональность и удобство использования Java «из коробки» оставляет желать лучшего.

Одним из главных свойств Java, обеспечившим ему огромную популярность, является переносимость. Философия Java провозглашает принцип «Write once - run everywhere», указывающий на то, что, в теории, Java-код может быть исполнен на любой JVM, работающей на любом устройстве. В реальной жизни, конечно, данный принцип работает с большими оговорками, однако это не мешает успешной работоспособности JVM более чем на трёх миллиардах устройств. Скорее всего, благодаря заманчивой перспективе переносимости, именно Java был выбран в качестве языка программирования для операционной системы (ОС) Android. Философия платформы включает в себя варианты использования ОС на любом устройстве с любой микропроцессорной архитектурой, будь то ARM, x86 или любая другая.

ОС Android является хорошим примером лукавости принципа «Write once - run everywhere»: ввиду ограниченности ресурсов использование стандартной JVM было затруднено, и единственным возможным выбором было использование Java ME JVM, разработанной для мобильных устройств. Однако, компания Google решила разработать собственную виртуальную машину Dalvik VM, которая отличается от других JVM следующим [3]:

.        Используется специальный формат DEX для хранения двоичных кодов с целью уменьшения их размера.

.        Dalvik VM оптимизирована для выполнения нескольких процессов одновременно.

.        Используется архитектуру, основанную на регистрах против стековой архитектуры в других JVM.

.        Используется собственный набор инструкций, а не стандартный байткод JVM.

.        Возможен запуск нескольких независимых Android-приложений в одном процессе.

.        Реализован специальный механизм сериализации объектов, основанный на классах Parcel и Parcelable.

.        Имеется особый способ для выполнения вызовов между процессами, основанный на Android Interface Definition Language (AIDL).

Кроме новой JVM, также были пересмотрены стандартные пакеты Java JDK API. Некоторые из ни были удалены, например, всё, что касалось Swing (библиотеки для создания графического интерфейса пользователя), но в том числе добавилось некоторое количество собственных android. пакетов. Таким образом, далеко не всякая программа или библиотека, написанная на Java, сможет без проблем быть скомпилированной и запуститься на Android-устройстве.

В связи с большой популярностью мобильных разработок, существует большое количество источников информации по Android-разработке. Основным источником, в первую очередь, является официальная документация [4]. Помимо технической документации комплекта средств разработки (Software Development Kit, SDK), официальный сайт содержит множество примеров, руководства по реализации стандартных компонентов, пошаговые рекомендации по разработке приложений с нуля, а также требования к дизайну.

Одной из наиболее популярных книг является [5]. Данная книга представляет собой практический курс по написанию программного обеспечения на базе второй версии Android SDK. Несомненно, книга сильно устарела на данный момент, однако всё ещё является хорошим структурированным руководством по изучению платформы. Поскольку множество приложений создается с условием совместимости с более старыми версиями ОС Android, многие примеры из данной книги не потеряли актуальности.

Как бы то ни было, из любого из выше приведенных источников можно почерпнуть сведения об архитектуре и процессе разработки приложений. Любое Android-приложение, содержит в себе хотя бы один из так называемых App Components - компонентов приложения. Существуют четыре вида основных компонентов приложения: Activity, Service, Content Provider и Broadcast Receiver. К сожалению, не существует какого-либо официального перевода этих терминов на русский язык. В переведённой литературе обычно используется калька с английского, например, Activity - «Активность». Поскольку термин «Активность» не совсем соответствует тому, для чего служит класс Activity (о его назначении будет написано ниже), в большинстве интернет-источников используется русская транскрипция терминов, например, Activity - «Активити».

Активити представляет собой одну «страницу» с пользовательским интерфейсом (если проводить аналоги с веб-интерфейсами). Все пользовательские активити являются классами, унаследованными от стандартного класса Activity. Здесь можно провести некоторую параллель с технологией Windows Forms, однако, поскольку Java не поддерживает частичные (partial) классы, в Android-приложениях используется иной способ связывания элементов интерфейса и пользовательского класса-активити. Каждая активити обладает определённым жизненным циклом (рисунок 1.1), который является хорошей демонстрацией специфики мобильных приложений. Поскольку ресурсы мобильных устройств весьма ограничены, достоверно можно сказать лишь то, что в памяти существуют лишь те объекты, которые так или иначе относятся к отображаемому на экране содержимому. Как только активити скрывается с экрана, например, если пользователь сворачивает приложение кнопкой «Домой», активити переходит в состояние «остановлено» (stopped). Начиная с этого момента, ОС может уничтожить все объекты в оперативной памяти, относящиеся к данной активити, включая саму активити. Исходя из этого очень важно вовремя сохранять состояние приложения. Второй особенностью активити является то, что каждый раз при смене ориентации устройства с ландшафтной на портретную или наоборот, активити уничтожается и создаётся заново. Сложно сказать, чем продиктовано подобное решение, скорее всего это сделано для того, чтобы обеспечить поддержку различных вариантов пользовательского интерфейса для различной ориентации устройства. Данная особенность порождает вторую важную проблему - проблему сохранения состояния при изменении конфигурации (например, при повороте экрана). Подробнее о работе с активити можно прочитать в специализированном разделе документации [4].

Сервис - это компонент, который работает в фоне и используется для длительных и ресурсоёмких операций. Отличительной особенностью сервиса является возможность запуска в привилегированном режиме (foreground mode), благодаря которому ОС не завершает приложение в фоне максимально долгое время. Несмотря на такую возможность, разработчикам крайне не рекомендуется злоупотреблять ею и наоборот, завершать сервисы как можно раньше. Привилегированный режим обычно используется для записи или воспроизведения потокового содержимого, а также для скачивания или загрузки файлов. Помимо упоминавшихся ранее источников, хорошим руководством по работе с сервисами является [6].

Ещё одним часто используемым компонентом является Broadcast Receiver (в русскоязычной литературе встречается перевод «широковещательный приемник»). Broadcast receiver - это компонент, служащий для обработки широковещательных сообщений. ОС Android использует специальные объекты для сообщения между компонентами - интенты (intent). Иногда в литературе встречается прямой перевод - «намерения». Интент - это объект класса Intent представляющий собой некоторое сообщение. С каждым интентом может ассоциироваться некий код ошибки, действие, URI (Uniform Resource Identifier), а также произвольный набор пересылаемых объектов. Объекты, так называемые extras, хранятся в ассоциативном контейнере с уникальными строковыми ключами. Поддерживаются все примитивные типы, строки, а также объекты, реализующие интерфейсы Serializable или Parcelable. Интенты могут быть узконаправленными, например, для запуска сервиса или активити, или же широковещательными, обычно используемые в качестве нотификации о каком-либо событии. Интенты широко используются в ОС Android: благодаря широковещательным интентам, с помощью широковещательных приемников можно подписываться на различные системные события, от срабатывания будильника и до перехода по ссылке в браузере.

Рисунок 1.1 - Жизненный цикл Activity

Последним нерассмотренным компонентом является поставщик контента - content provider. Это, пожалуй, наименее часто используемый компонент, служащий для предоставления доступа к некоторому хранилищу данных. Существуют системные поставщики контента для доступа к телефонным контактам, получения информации об установленных приложениях и многого другого. Доступ осуществляется с помощью URI по аналогии с доступом к веб-страницам. Низкая популярность данного компонента связана с его специфичностью: далеко не каждое приложение имеет необходимость в предоставлении доступа к своим данным.

Широковещательные приёмники и поставщики контента хорошо описаны во многих источниках, например, на популярном русскоязычном ресурсе, посвящённом изучению программирования для ОС Android - [7].

Связующим элементом, объединяющим разрозненные компоненты в единое Android-приложение, является манифест. Манифест - это специальный обязательный XML-файл, содержащий перечень всех компонентов приложения, минимальную и используемую для сборки версии ОС, а также требуемые для работы разрешение и необходимые возможности устройства (например, наличие фронтальной камеры или определённой версии OpenGL ES).

Помимо изучения работы с основными компонентами, каждый разработчик должен ознакомится с основными шаблонами проектирования ОС Android. Наиболее часто используемыми стандартными шаблонами проектирования являются наблюдатель (observer) и слушатель (listener). Также широко используются такие принципы как обмен сообщений и позднее связывание. Помимо этого, Android диктует свои правила при реализации некоторых стандартных процессов, тем самым заставляя разработчикам следовать определённым специфическим шаблонам проектирования. Ярким примеров этого могут служить загрузчики (loaders). Загрузчики являются потомками класса Loader и служат для асинхронной загрузки данных. Главным преимуществом загрузчиков является то, что их жизненный цикл не зависит от других компонентов. Для доступа к загрузчикам используется специальный компонент Loader Manager, который позволяется работать с загрузчиками любым классам, реализующим интерфейс Loader Manager Loader Callbacks. Несмотря на некоторые встроенные во фреймворк реализации, разработчикам довольно часто приходится реализовывать свои специфические загрузчики, например, для работы с локальной базой данных или получения массивов информации по сети. Хорошую статью о практической реализации загрузчиков можно найти по адресу [8]. Там же можно отыскать и другие полезные статьи по специфическим шаблонам проектирования.

Несмотря на довольно динамичное развитие SDK и внедрение новых шаблонов проектирования, в последнее время издавалось не много литературы с упором на актуальные версии средств разработки. В качестве одного из примером, можно привести [9]. Книга во много схожа с [2], однако базируется на современной версии платформы и отдельно затрагивает тему обеспечения совместимости с использованием стандартных компонентов.

Поскольку Android-разработка является фрэймворк-ориентированной, то есть, достаточно сильно завязанной на стандартные классы и генерацию кода, перед всеми разработчиками встаёт вопрос: какую же среду разработки следует использовать для максимально удобного программирования под Android? Однозначного ответа на него нет, скорее это вопрос личных предпочтений. Существует четыре основных интегрированных среды разработки (Integrated development environment, далее IDE) с поддержкой Android-проектов:

- Eclipse + ADT Plugin;

-   InelliJ IDEA;

-   NetBeans;

-   Android Studio.

Долгое время чаще всего использовались первые две IDE. Eclipse является скорее платформой для создания универсальных сред разработки, основанных на интегрировании специальных модулей расширения - плагинов. Соответствующий плагин для Android-разработки носит имя ADT Plugin и обладает весьма обширным функционалом. Первоначально именно его широкая функциональность принесла популярность связке Eclipse + ADT Plugin, поскольку она позволяла, например, редактировать пользовательский интерфейс в WYSIWYG стиле. Несмотря на это, в последнее время Eclipse сильно уступила InelliJ IDEA в связи с бурным и динамичным развитием последней. InelliJ IDEA также поддерживает расширения в виде плагинов и позволяет создавать Android-проекты, однако объективно обладает намного более впечатляющими возможностями для написания, редактирования и рефакторинга исходного кода по сравнению с Eclipse.

Наименее распространённой IDE является NetBeans, поскольку она используется в основном пользователями Linuх, которые ранее применяли NetBeans для своих проектов.

На данный момент весьма перспективно выглядит IDE от Google - Android Studio. Она позиционируется как специализированное средство для Android-разработки от компании разработчика самой ОС. Android Studio основывается на InelliJ IDEA и обладает всеми её достоинствами. В добавок к этому Android Studio содержит специализированные средства разработки и анализа кода, специфичные для Android-разработки. Пожалуй, единственным недостатком Android Studio является то, что она официально всё ещё находится в стадии бета-тестирования. При разработке данного проекта использовалась именно эта IDE. Подробнее ознакомится с данным продуктом можно с помощью [10]. Там же можно найти подробные описания процесса миграции проектов на новою IDE с других популярных решений.

Отличительной особенностью Android Studio является использование постепенно набирающей популярность системы сборки Gradle. Её описание можно найти на официальном сайте [11] или же на ресурсе [12], применительно конкретно к Android Studio. К особенностям Gradle, выгодно отличающей её от других систем сборки, стоит отнести сплав концепции «build by convention», используемой во всех популярных аналогах, например, Maven, с использование скриптов на языке Groovy. Подобный подход позволяет легко создавать многомодульные сборки, что весьма актуально для Android-проектов, использующих пользовательские библиотеки. Ещё одной возможностью Gradle, активно используемой в Android Studio является возможность создавать основанные на Gradle проекты, при этом фактически всё, что необходимо - это пара стандартных скриптов для построения и скачиваемый из общедоступного репозитория модуль для их обработки. Подобная лаконичная структура весьма эффектно смотрится при использовании системы контроля версий: фактически все файлы проекта являются функционально необходимыми и не захламляют репозиторий.

Стоит отметить, что все выше перечисленные среды разработки либо являются полностью бесплатными, либо имеют бесплатные версии.

В заключении следует упомянуть некоторые источники, специфичные для данного дипломного проекта. В проекте активно используется взаимодействие с базой данных (БД). На ОС Android в качестве БД использует SQLite. SQLite является простой, легковесной реляционной базой данных, используемой на всех популярных мобильных платформах (iOS, Windows Phone). Помимо официальной документации [4], описывающей шаблон проектирования для работы с БД, полезным является официальная документация БД [13]. SQLite обладает определёнными особенностями, ознакомление с которыми необходимо даже при наличии опыта работы с другими реляционными БД. SQLite содержит всего лишь четыре типа данных и не поддерживает хранимые процедуры. Вдобавок к этим ограничениям, добавляется и то, что входящие в Android SDK средства для работы БД не способствуют созданию даже самой простой структуры БД с зависимостями между таблицами, всячески подталкивая к хранению данных в нескольких больших, несвязанных таблицах.

Одной из ключевых особенностей дипломного проекта, является работа с визуальной информацией, то есть использование камеры. Несмотря на исчерпывающее описание процесса работы с камерой как в официальной документации, так и в литературе, камера остаётся весьма специфическим устройством. В основном это связано с большим разнообразием используемых фотомодулей, так или иначе влияющим на стандартный процесс взаимодействия. К сожалению, не существует единого источника, описывающего все, или хотя основные, подводные камни в работе с камерой, однако в качестве полезнейшего ресурса, во многом помогающего разработке, в том числе и в вопросах взаимодействия с камерой, стоит привести [14].

Следует отметить, что отличительной чертой Android-разработки, с точки зрения обзора литературы, является большое число интернет-ресурсов. Это обусловлено, во-первых, популярностью платформы, а, во-вторых, особенностями мобильной разработки. Несмотря на наличие официальной документации и нескольких основных книг по разработке, на практике существует большое число нюансов, которые обычно не рассматриваются в официальных источниках. В качестве примера таких нюансов можно привести решения для обхода ошибок в Android SDK или же специфических для устройств определённых производителей особенностей платформы.

2. Системное проектирование

Типичное Android-приложение имеет модульную структуру, зачастую с весьма слабосвязанными компонентами, общающимися посредствам пересылки сообщений. В данном дипломном проекте можно выделить четыре основных компонента, три из которых ассоциированы с основными активити приложения, а четвёртый отвечает за фоновую обработку данных. Наряду с основными модулями, которые логически выделяются исходя из входных требований к приложению, несложно определить набор дополнительных модулей. К ним можно отнести модуль настроек, присутствующий в большинстве Android-приложений, а также пользовательский интерфейс для взаимодействия с ним. На первый взгляд, выделение двух модулей может показаться излишним, однако исходя из специфики взаимодействия с настройками, оно довольно логично. Приложение также использует БД для кеширования части данных с сервера, поэтому логично будет отдельный модуль для работы с БД. Приложение обладает небольшим количеством фоновых операций, поэтому, по крайней мере на данном этапе, нет необходимости в выделении отдельных сервисов для работы с БД и веб-сервером. Как было сказано выше, Android позволяет использовать отдельные компоненты-загрузчики для асинхронного получения массивов данных. Данные загрузчики имеют автономный жизненный цикл и являются внешними компонентами по отношению к модулю взаимодействия с БД, поэтому их разумно выделить в отдельный модуль. Приложение обладает небольшим количеством фоновых операций, поэтому, по крайней мере, на данном этапе, нет необходимости в выделении отдельных сервисов для работы с БД и веб-сервером. Поэтому, чтобы подчеркнуть функции фонового сервиса, как контекста выполнения операций, веб-клиент также следует вынести в отдельный модуль. Таким образом, окончательная структура дипломного проекта представлена на чертеже ГУИР.400201.086 C1.

Рассмотрим по подробнее каждый из модулей. Модуль авторизации, как и прочие два модуля, ассоциированные с активити, состоит из двух частей, одной из которых является представление, а другой класс-контроллер, унаследованный от класса Activity. Несмотря на это, не следует выделять эти части в два разных модуля, как это можно было бы сделать в случае веб-приложения. Представление, по своей сути, является набором декларативных XML-файлов, описывающих разметку. Несмотря на то, что эти файлы подгружаются динамически классом-контроллером, между ними всё ещё существует неявная, односторонняя, но достаточно существенная связь. В то время как представление ничего не знает о классе-контроллере, да и вообще о структуре классов в целом (исключениями в некотором роде являются пользовательские элементы управления), класс-контроллер должен оперировать строго определёнными элементами представления. Попытка получить элемент, отсутствующий в разметке, неизбежно приведёт к исключению во время исполнения. Кроме этого, следует учитывать, что для получения объектов, ассоциированных с элементами управления используется метод find View ById, принимающий на вход уникальный числовой идентификатор, определённый в разметке, и возвращающий базовый тип View. Таким образом, каждый объект, полученный с помощью данного метода, необходимо приводить к конкретному типу, например, Button или List View, многие из которых обладают уникальными свойствами. Это создаёт ещё одно опасное место, способствующее возникновению ошибок, связанных с приведением типов, и выдаче исключений Class Cast Exception во время исполнения. Помимо этого, существует возможность программно создавать элементы управления и изменять разметку, что способствует более тесной связи представления и класса-контроллера.

Активити авторизации является первым, что видит пользователь приложения. Помимо своих основных функций (предоставления возможности для ввода логина и пароля, а также их валидации) данный модуль также выполняют функцию определения первого запуска приложения. Архитектурой Android-приложения не предусмотрен метод, который вызывается при самом первом старте приложения, или же хотя бы просто при старте приложения. Вследствие этого, зачастую обработка подобных ситуаций производится в начальной активити приложения, то есть, в данном случае, в модуле авторизации. Кроме того, активити авторизации является единственной в приложении, где используются разные разметки для альбомной и портретной ориентации экрана.

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

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

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

Достаточно обособленным выглядит модуль работы с БД. Во многом это следствие шаблонов проектирования ОС Android. Платформа предоставляет возможности для автономной инициализации БД при первом запуске приложения, а также позволяет вести версионность БД. При этом, в общем случае, имеется возможность реализации поставщика содержимого (Content Provider), предоставляющий внешний интерфейс для взаимодействия с БД. Поставщик содержимого можно трактовать как аналог сервиса, представляющего публичный API для любых внешних приложений. Конечно, существует возможность ограничения доступа к поставщику. Можно дать права на подключения лишь некоторым приложениям или требовать запроса разрешений от внешних приложений, которые планируют контактировать с поставщиком, или же ограничить видимость поставщика собственных приложением. В последнем случае, фактически, необходимость в поставщике содержимого отпадает, и программист волен сам решать, как реализовывать интерфейс для взаимодействия с БД. В данном проекте БД используется для кеширования данных сервера и нет необходимости делать доступными эти данные для внешних компонент. Модуль работы с БД предоставляет набор открытых методов для асинхронного обновления содержимого БД, а также предоставляет возможность для подписки класса-обозревателя на события от БД. В данном случае, такими событиями являются факты получения новых данных. В качестве обозревателей используются прокси-объекты, являющиеся частью модулей загрузки данных. Процесс подключения к БД для чтения данных выглядит следующим образом:

.        Модуль-инициатор чтения (в данном случае, модуль редактирования метаданных) запрашивает у менеджера загрузчиков создание нового объекта-загрузчика, указывая события, к которым будет подключен класс обозреватель, а также класс-слушатель, вызываемый по готовности данных.

.        Создаётся новый автономный загрузчик, который, через прокси-объект, подписывается на события обновления определённых данных в БД. Если менеджер загрузчиков видит, что необходимый загрузчик уже был создан, он отдаёт существующий вместо создания нового.

.        По готовности загрузчика, а также при последующих событиях от БД вызывается метод обратного вызова в классе-инициаторе чтения, в который передаются полученные данные.

Стоит отметить, что менеджер загрузчиков - это системный объект класса Loader Manager, который служит для взаимодействия с конкретными загрузчиками (наследниками класса Loader), а также обеспечивает поддержку их автономного жизненного цикла. В то же время, конкретные загрузчики являются пользовательскими классами, реализующими необходимые методы для обработки данных, а также предоставляющие возможность подписки на произвольные события извне.

Ещё одним не видным пользователю модулем является веб-клиент. В данном случае, это класс реализующий взаимодействие с веб API на клиентской стороне. Основными его функциями являются установка соединения с сервером, вызов удалённых веб-методов, а также преобразование структур данных от используемых приложением абстракций к установленному формату веб API. Основной причиной вынесения веб-клиента в отдельный модуль является универсальность модуля обработки фоновых операций. Принимая во внимание необходимую функциональность разрабатываемого приложения, не имеет смысла выделение нескольких сервисов для различных типов (работа с сетью, работа с БД) фоновых операций. Подобное разделение вызвало бы лишь излишнюю сложность при синхронизации работы компонент и пересылке данных. Таким образом, выделение отдельных компонент - веб-клиента и модуля работы с БД - подчёркивает их специализацию, а также указывает на роль модуля фоновой обработки данных, как контекста выполнения фоновых операций.

Модуль фоновой обработки операций построен на базе Android-сервиса и предназначен, как следует из названия, для осуществления затратных по времени операций вне основного потока выполнения. Помимо основного своего назначения, модуль определяет слабосвязанный интерфейс для обмена с остальными крупными модулями приложения через интенты. Типичный ход работы приложения не определяет параллельных операций, поэтому сервис используется как очередь для обработки ресурсоёмких операций в отдельном потоке. Для этого используется входящий в состав Android SDK класс Handler. С помощью наследования от этого класса, можно получить готовую очередь событий с пользовательским обработчиком сообщений - объектов класса Message, способных передавать в обработчик все необходимые данные. Экземпляр подкласса Handler может быть ассоциирован с любым потоком, что позволяет организовывать межпотоковую коммуникацию для обеспечения обратной связи с основным потоком исполнения. Несмотря на это, в данном проекте используется иной способ коммуникации, основанный на интентах. Подобный подход позволяет более просто организовать доступ к сервису из нескольких модулей приложения. К счастью, ОС Android предоставляет частичную реализацию подобного шаблона обработки сообщений - класс Intent Service. Объект данного класса представляет собой Android-сервис, который при старте запускает отдельный фоновый поток исполнения и ассоциирует с ним объект Handler, оставляя пользователя право самому определить, как будут обрабатываться полученные интенты. Если один или несколько интентов приходят во время обработки предыдущих, то они автоматически добавляются в конец текущей очереди обработки операций. IntentService самостоятельно помещает интенты в объекты-сообщения для постановки в очередь обслуживания и извлекает их напрямую передавая в метод-обработчик. Особенностью данной реализации является то, что Inten Service поддерживает лишь одну очередь обработки сообщений, а также автоматически завершается после выполнения всех операций из очереди для экономии ресурсов телефона. Данные особенности вынуждают отказываться от использования Intent Service в случаях, когда требуется параллельная обработка операций или же обеспечение долгоживущего сервиса, однако для целей текущего проекта Intent Service подходит идеально.

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

Первая цепочка представлена следующим набором последовательных операций:

.        Пользовательский ввод.

.        Реакция на ввод в активити.

.        Отправка команды сервису фоновой обработки.

.        Вызов удалённого веб API.

.        Получение данных в формате JSON.

.        Преобразование данных к виду, используемому на клиенте.

.        Передача данных для обновления в БД.

С другой стороны, вторая цепочка описывается следующим образом:

.        Старт активити.

.        Создание загрузчика и подписка его на обновления БД.

.        Ожидание обновления и передача данных в активити по готовности.

.        Переход к пункту 3 для отслеживания актуальности данных.

Стоит отметить, что цепочки независимы друг от друга и инициируются различными модулями. Кроме того, одновременно запускается сразу несколько цепочек второго типа, при этом они могут начать своё выполнение не дожидаясь окончания первой цепочки. Данная схема является работоспособной за счёт того, что операции обновления БД инициируются модулем фоновой обработки операций, а операции чтения данных из БД выполняются модулями загрузки данных после того, как модуль работы с БД выдаст событие об успешном обновлении данных.

3. Функциональное проектирование

В данном разделе подробно рассматривается функционирование программных модулей. Перечислены все присутствующие классы и большинство их компонентов. Состав и отношения классов также показаны на диаграмме классов (чертёж ГУИР.400201.128 РР.2).

.1 Описание взаимодействия с сервером

Одной из главных функций мобильного клиента является обмен данными с сервером. Для этих целей был разработан интерфейс, описывающий набор веб-методов для коммуникации между компонентами. Иными словами, интерфейс программирования приложений (application programming interface, далее API). API базируется на трёх основных принципах:

.        Формат передаваемых данных - JSON.

.        Информация о пользователе передается с каждым запросом при помощи базовой аутентификации HTTP (Basic Authentication).

.        Для отправки данных используются HTTP POST запросы.

На данный момент сервер предоставляет пять веб-методов доступных клиенту, описание которых представлено ниже. Методы перечислены в порядке их вызова приложением. Как только пользователь нажимает кнопку «Войти» на экране авторизации, вызывается метод login для проверки прав доступа. Если доступ получен, приложение по очереди вызывает методы stores и categories для обновления локального кэша, в то время как пользователь может делать снимок. После того как пользователь сделает фото, он попадает на страницу метаданных, заполнив которые, отправляет фото на сервер, другими словами производится вызов методов upload_photo и save_photo.

.        Авторизация пользователя (../api/login)

Входные данные:

{

}

Ответ сервера:

{

"role": "user"

}

Описание:

Данный метод используется для первоначальной авторизации пользователя и получение его роли, которая может быть использован в дальнейшем. На текущий момент существует три роли: «user», «merchandizer» и «admin». Любая из данных ролей позволяет пользоваться приложением, хотя в дальнейшем это поведение может быть изменено, в частности, некоторые функции могут стать недоступными для некоторых ролей.

.        Получение списка магазинов (../api/stores)

Входные данные:

{

"version": 10

}

Ответ сервера:

 [

{

"id": 102,

"version": 11,

"active": true,

"name": "Coolman",

"street": "Кульман",

"city": "Минск"

},

...

]

Описание:

Метод возвращает список магазинов, обновившихся после запрошенной версии (поле «version» из входных данных). При каждом изменении данных о магазине на сервере должен увеличиваться номер версии. Номер версии является глобальным для клиента и сервера счетчиком. Клиент при запросе отправляет максимальную версию из имеющихся у него. Важной особенностью системы является то, что данные о магазинах никогда не удаляются, однако им можно присваивать неактивный статус (поле «active» из ответа сервера). Неактивные магазины не отображаются при выборе точки продажи на клиенте.

.        Получение списка категорий (..api/categories)

Входные данные:

{

"version": 10

}

Ответ сервера:

 [

{

"id": 3,

"version": 6,

"active": true,

"name": "Computers"

},

...

]

Описание:

Метод возвращает список категорий, обновившихся после запрошенной версии. (поле «version» из входных данных). Метод аналогичен предыдущему, но вместо списка магазинов получает список категорий товаров. Номер версии также является глобальным для клиента и сервера, а категории, аналогично магазинам, не удаляются, а переводятся в неактивный режим.

.        Загрузка фотографии (../api/upload_photo)

Входные данные:

Фото в бинарном виде.

Ответ сервера:

{

"file": "3F2504E0-4F89-11D3-9A0C-0305E82C3301"

}

Описание:

Метод используется для загрузки фотографий на сервер. После загрузки сервер генерирует уникальный идентификатор для файла (GUID) и сохраняет его под этим именем в хранилище, после чего возвращает идентификатор в качестве ответа на запрос. Клиент после успешной загрузки файла использует идентификатор для передачи дополнительной информации, связанной с файлом при вызове следующего метода.

.        Сохранение фотографии (../api/save_photo)

Входные данные:

{

"store": 102,

"category": 12,

"comment": "User defined comment",

"problem": false,

"was_taken": "unknown"

"file": "3F2504E0-4F89-11D3-9A0C-0305E82C3301",

"coordinates": {

"latitude": 54.3,

"longitude": 23.4,

"accuracy": 10

}

}

Ответ сервера:

{

"success": true

}

Описание:

Метод используется для сохранения загруженной ранее фотографии с идентификатором, переданным в качестве параметра «file» запроса. В данном случае в теле POST-запроса передаются метаданные снимка, которые включают в себя следующие элементы:

- идентификатор магазина («store»);

-   идентификатор категории («category»);

-   произвольный комментарий («comment»);

-   специальное поле «was_taken», позволяющее определить снимок как сделанный до, либо после работы; возможные значения: «unknown» (по умолчанию), «before» или «after»;

-   идентификатор файла («file»);

-   координаты точки, в которой была сделана фотография (структура «coordinates»), включающие в себя широту («latitude»), долготу («longitude») и точность определения («accuracy»).

В случае неправильного логина или пароля, любой из выше перечисленных методов возвращает ошибку в виде HTTP ответа с кодом 401 - «Unauthorized». Ответ сервера сделан непустым на случай, если понадобится добавление расширенных сообщений об ошибках.

3.2 Описание классов системных компонентов

Классы системных компонентов, применительно к данному проекту, представляют собой класс Worker Service, наследованный от класса Intent Service, а также четыре класса, унаследованные от класса Acivity: Login Activity, Settings Acivity, Camera Activity и Data Activity. Особенностью данных классов является то, что они описывают тот или иной крупный компонент приложения, важный, в первую очередь, для ОС.

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

-   void onCreate(Bundle savedInstanceState) - метод, вызываемый при создании активити. В нём осуществляется создание и размещение (inflatting) визуальных компонентов (обычно из специального файла с разметкой), а также начальная инициализация компонентов. Данный метод можно рассматривать как аналог конструктора, с той лишь разницей, что на момент вызова активити будет уже не только создана, но и отображаться на экране телефона. Объект типа Bundle является контейнером, содержащим пары ключ-значение и служит для восстановления параметров при пересоздании активити после поворота экрана или иной смены конфигурации.

-   boolean on Create Options Menu (Menu menu) - метод, вызываемый при создании контекстного меню активити, пункты которого добавлялись к объекту menu. На данный момент шаблон проектирования с использованием контекстного меню устарел и в этом методе производится инициализация так называемой «строки действий» (Action Bar), являющей актуальным на данный момент дизайнерским шаблоном для предоставления контекстно зависимых опций. В отличие от контекстного меню, строка действий, обычно, инициализируется не в коде, а в файле разметки. Возвращаемое методом значение служит индикатором для создания меню: если метод вернёт false, то контекстное меню создано не будет, даже если оно будет инициализировано значениями. Такое поведение справедливо также и для строки действий.

-   boolean on Options Item Selected (MenuItem item) - метод вызываемый при выборе пункта меню или действия со строки действий (в действительности, это одно и то же: разница лишь в терминологии и способе отображения). Объект типа Menu Item ассоциирован с визуальным элементом, обрабатывающим нажатие, а возвращаемое значение является индикатором окончания обработки нажатия: если метод возвращает false, для определённого пункта меню, значит нажатие на него будет обработано в другом месте.

-   void on Resume() - метод, вызываемый непосредственно перед отображением активити на экране. В данном методе обычно производится возобновление всех временно прерванных процессов.

-   void on Pause() - метод, вызываемый при переходе активити в неактивный режим. Иными словами, каждый раз, когда активити пропадает с экрана. В данном методе обычно приостанавливаются все связанные с пользовательским интерфейсом процессы, а также производится сохранение состояния важных переменных.

-   void on Activity Result (int request Code, int result Code, Intent data) - данный метод представляет собой обработчик результатов, полученных от действий инициируемых с помощью интентов, например, таких, как вызов активити, содержащей детальное представление или получение информации от датчиков телефона. В данном случае request Code - уникальный идентификатор интента, result Code - код результата, обычно показывающий успех или ошибку, data - непосредственно сам интент с результатами выполнения запроса.

-   void on Click (View view) - данный метод не является частью класса Activity, однако очень часто используется в его наследниках. Данный метод является единственным компонентом интерфейса View. On Click Listener, который должны реализовать объекты-слушатели событий-кликов по визуальным элементам управления. Существует два основных подхода к обработке кликов, каждый из которых имеет свои преимущества и недостатки: либо для каждого элемента управления создаётся свой класс-слушатель (обычно анонимный), либо активити реализует интерфейс View. On Click Listener и становится слушателем для всех свои элементов управления. В данном проекте используется второй способ.

Помимо этих методов, можно отметить наличие в классах-активити целочисленных констант вида «ACTION_CODE_XXX» и строковых констант вида «YYY_DIALOG_TAG».

Константы первого типа представляют собой уникальные ключи, используемые при создании интентов. Они необходимы для идентификации результатов, полученных в качестве ответа на определённые интенты. Идентификатор, заданный при создании интента будет в последствии передан в метод on Activity Result в качестве параметра requestCode. Значения целочисленных констант формируются из трёх цифр наподобие кодов HTTP ответов. Первая цифра показывает компонент (класс), которому принадлежит константа, вторая - область применения константы (идентификатор интента, идентификатор загрузчика и т.д.), третья - порядковый номер константы в рамках заданного компонента и области применения.

Константы второго типа представляют собой идентификаторы-теги диалоговых окон. Согласно новым шаблонам проектирования, диалоговые окна представляют собой фрагменты, унаследованные от базового класса Dialog Fragment. Это сделано для того, чтобы не сохранять состояние диалоговых окон каждый раз, когда пользователь поворачивает экран или сворачивает приложение нажатием кнопки домой. Диалоговые окна, основанные на фрагментах, имеют свой жизненный цикл, не зависящий от жизненного цикла активити, поэтому они могут быть созданы одним экземпляром класса, а использоваться другим. Для того, чтобы не было путаницы, каждый диалоговый фрагмент отмечается тегом, по которому его всегда можно найти.

Класс Login Activity

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

Константы:

- ACTION_CODE_LOGIN - идентификатор интента, ассоциированного с действием авторизации.

-   PROGRESS_DIALOG_TAG - тег диалогового фрагмента, используемого для отображения прогресса выполнения задачи.

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

-   REDIRECT_DIALOG_TAG - тег диалогового фрагмента, используемого для переадресации на страницу на строек при первом запуске приложения.

-   SAVE_CREDENTIALS_PROPERTY - константа-ключ для доступа к опции запоминания логина и пароля.

Поля:

- Save Credetials - поле типа boolean, содержащее актуальное значение опции запоминания логина и пароля.

-   Login Edit Text - поле типа Edit Text, содержащее ссылку на текстовое поле для ввода имени пользователя (логина).

-   Password Edit Text - поле типа Edit Text, содержащее ссылку на текстовое поле для ввода пароля.

-   Save Credentials Check Box - поле типа Check Box, содержащее ссылку на флажок опции запоминания логина и пароля.

-   Signin Button - поле типа Button, содержащее ссылку на кнопку «Войти».

-   Progress Dialog - поле типа Progress Dialog Fragment, содержащее ссылку на диалоговый фрагмент, используемый для отображения прогресса выполнения задачи.

-   Preferences - поле типа Shared Preferences, содержащее ссылку на модуль настроек.

Методы:

- void try Begin Authorize User() - метод, осуществляющий валидацию пользовательского ввода перед операцией авторизации, а также отображающий результаты валидации.

-   void begin Authorize User (String login, String password) - метод, запускающий процесс асинхронной авторизации. В качестве входных параметров выступают введённые пользователем логин и пароль.

-   void end Authorize User(String role) - метод, вызываемый при окончании авторизации. Входным параметром является роль пользователя.

-   void navigate To Camera Activity () - метод, осуществляющий переход к активити управления камерой.

-   void before Text Changed (Char Sequence char Sequence, int i, int i2, int i3) - метод интерфейса Text Watcher. Не используется.

-   void on Text Chan ged (Char Sequence char Sequence, int i, int i2, int i3) - метод интерфейса Text Watcher. Не используется.

-   void after Text Changed (Editable editable) - метод интерфейса Text Watcher. Вызывается послед ввода каждого символа. Разблокирует кнопку signin Button при наличии логина и пароля.

-   boolean on Editor Action (Text View text View, int i, Key Event key Event) - метод интерфейса Text Watcher. Данный метод обрабатывает нажатия специальных кнопок клавиатуры, например, кнопки «Далее».

-   void report Error (int message Id) - метод, осуществляющий отображения диалога с информационным сообщением. Сообщение хранится в ресурсах приложения, в то время как метод принимает идентификатор ресурса - message Id.

-   void show Redirect Dialog () - метод, осуществляющий отображения диалога для перенаправления на страницу настроек.

-   void restore Properties () - метод, осуществляющий восстановление ранее введённых данных.

-   void save Preferences () - метод, осуществляющий сохранение текущего состояния активити.

-   void on Click (Dialog Interface dialog Interface, int i) - метод интерфейса Dialog Interface. On Click Listener, вызываемый при нажатии на кнопку диалогового окна.

Класс SettingsActivity

Данный класс ассоциирован с активити настроек и служит для отображения и работы с опциями приложения. Класс содержит единственный метод void on Create (Bundle saved Instance State), описанный выше. В данном методе создаётся фрагмент Settings Fragment, который заполняет всю полезную площадь активити.

Фрагмент Settings Fragment, в свою очередь, является наследником специального класса Preference Fragment, предоставляющего удобные средства для работы с настройками приложения. Класс Settings Fragment также содержит единственный метод void on Create (Bundle saved Instance State), сходный по назначению с вышеописанным методом активити. В данном методе фрагмент создаёт и размещает элементы управления на основе специального XML-файла, описывающего разметку. Помимо этого, здесь же инициализируются обработчики событий для визуальных элементов, представляющих ту или иную опцию.

Класс Camera Activity

Данный класс ассоциирован с активити управления камерой. Основными его функциями являются отображение на экране видеопотока с камеры, работа с настройками камеры, получение снимков, а также обработка поворотов экрана телефона.

Константы:

- PROPERTY_SIZE_INDEX - константа-ключ для доступа к опции размера изображения.

-   PROPERTY_FLASH - константа-ключ для доступа к опции вспышки.

Поля:

- camera - поле типа Camera, содержащее ссылку на объект-камеру.

-   cameraId - поле типа Camera, содержащее идентификатор камеры.

-   Bottom Panel - поле типа Linear Layout, содержащее ссылку на панель с кнопками выбора снимка.

-   Bottom Panel Hidden use Auto Focus - флаг типа boolean, показывающий состояние панели bottom Panel.

-   Camera Button Layout - поле типа Relative Layout, содержащее ссылку на панель-контейнер для кнопки снимка.

-   Camera Button - поле типа Button, содержащее ссылку на кнопку снимка.

-   Camera IconImage - поле типа Image View, содержащее ссылку на иконку кнопки снимка.

-   Orientation Observer - поле типа Orientation Event Listener, содержащее ссылку на обозреватель смены ориентации дисплея.

-   Icon Orientation - поле типа int, содержащее текущую ориентацию дисплея.

-   Picture Sizes - поле, содержащее массив элементов типа Camera. Size, соответствующих трём (или менее) минимальным разрешениям камеры телефона.

-   Picture Size Index - поле типа int, содержащее позицию текущего разрешения снимка в списке pictureSizes.

-   preferences - поле типа Shared Preferences, содержащее ссылку на модуль настроек.

-   Use Auto Focus - флаг типа boolean, указывающий на возможность использования автофокуса при съёмке.

-   Use Flash - флаг типа boolean, указывающий на необходимость использования вспышки при съёмке.

-   Has Flash - флаг типа boolean, указывающий на наличие вспышки у телефона.

-   Action Bar - поле типа ActionBar, содержащее ссылку на панель меню.

-   Settings Button - поле типа Button, содержащее ссылку на кнопку настройки разрешения снимка.

-   Settings Image View - поле типа Image View, содержащее ссылку на иконку кнопки настройки разрешения снимка.

-   Log Out Button - поле типа Button, содержащее ссылку на кнопку выхода из аккаунта.

-   Log OutImage View - поле типа Image View, содержащее ссылку на иконку кнопки выхода из аккаунта.

-   Flash Button - поле типа Button, содержащее ссылку на кнопку настройки вспышки.

-   Flash Image View - поле типа Image View, содержащее ссылку на иконку кнопки настройки вспышки.

-   Popup Window - поле типа Popup Window, содержащее ссылку на всплывающее окно со списком доступных разрешений снимка.

-   Popup Window Center - поле типа int, содержащее горизонтальную координату центра всплывающего окна popupWindow.

-   Surface Holder - поле типа Surface Holder, содержащее ссылку на специальный объект для взаимодействия с визуальным компонентом для отображения потокового видео.

Методы:

- int find Back Facing Camera () - метод, осуществляющий поиск идентификатора основной камеры телефона.

-   Camera try Get Back Camera () - метод, возвращающий объект-камеру, при условии её наличия.

-   void close Camera () - метод, высвобождающий объект-камеру.

-   int get Rotation Value () - метод, осуществляющий

-   boolean get Flash Availability () - метод, вычисляющий угол поворота камеры.

-   Camera. Size[] get Picture Sizes (Camera camera) - метод, возвращающий список из трёх или менее минимальных разрешений камеры.

-   void set Picture Size (int index) - метод, устанавливающий одно из разрешений из списка picture Sizes.

-   void set Flash Mode(boolean use Flash) - метод, включающий и выключающий вспышку.

-   void set Up Camera Parameters () - метод, осуществляющий установку необходимых для съёмки параметров камеры.

-   void reset Camera Preview (Surface Holder holder) - метод, осуществляющий переинициализацию визуального компонента для вывода видеопотока с камеры.

-   void onItem Click (Adapter View<?> adapter View, View view, int i, long l) - метод интерфейса List View. On Item Click Listener, осуществляющий обработку кликов по элементам списка List View.

-   void navigate To Login Activity () - метод, осуществляющий переход к активити авторизации.

-   void navigate To Data Activity () - метод, осуществляющий переход к активити метаданных.

-   void surface Created (Surface Holder surface Holder) - метод интерфейса Surface Holder. Callback, вызываемый при изменении размеров элемента управления Surface View.

-   void surface Changed (Surface Holder surface Holder, int i, int i2, int i3) - метод интерфейса Surface Holder. Callback, вызываемый при уничтожении элемента управления Surface View.

-   void surface Destroyed (Surface Holder surface Holder) - метод интерфейса Surface Holder. Callback, вызываемый при создании элемента управления Surface View.

-   void on Picture Taken (byte[] bytes, Camera camera) - метод интерфейса Camera. Picture Callback, вызываемый при получении снимка.

-   void on Auto Focus (boolean b, Camera camera) - метод интерфейса Camera. Auto Focus Callback, вызываемый при срабатывании автофокуса.

-   byte[] correctImage(byte[] data) - метод, преобразующий полученный с камеры снимок к формату JPEG.

-   Bitmap rotate Image (Bitmap bitmap, int degree) - метод, осуществляющий поворот фотографии в соответствии с ориентацией дисплея.

-   void swap Elements Visibility() - метод, осуществляющий смену элементов управления между режимами съёмки и сохранения снимка.

-   void rotate Popup (int to Orientation) - метод, осуществляющий поворот всплывающего окна po pupWindow.

-   void try Start Rotation (View view, int toOrientation) - метод, осуществляющий поворот одной иконки.

-   void rotateImages (int to Orientation) - метод, осуществляющий поворот всех иконок.

-   Rotate Animation get Animation (int to Orientation) - метод, возвращающий объект-анимацию для поворота иконок.

-   void update Flash Icon() - метод, осуществляющий отображение корректной иконки, отображающей режим вспышки.

-   void set Up Orientation Event Listener() - метод, возвращающий объект-слушатель orientation Observer.

-   Popup Window set Up Popup Window() - метод, создающий дочерние элементы управления для всплывающего окна popup Window.

-   Array List<String> get Human Readable Headers() - метод, преобразующий набор разрешений камеры к понятному для человека виду.

-   void set Popup List Content (List View list, final Array List <String> human Readable Headers) - метод, заполняющий значениями всплывающее окно popup Window.

-   Popup Window get Popup Window (View view, int width, int height) - метод, создающий всплывающее окно popup Window.

Класс Data Activity

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

Константы:

- ACTION_CODE_UPLOAD - идентификатор интента, ассоциированного с действием загрузки фото.

-   ACTION_CODE_SAVE - идентификатор интента, ассоциированного с действием отправки метаданных и сохранения фото.

-   ACTION_CODE_LOCATION - идентификатор интента, ассоциированного с действием получения координат.

-   CITIES_LOADER - идентификатор загрузчика списка городов.

-   STREETS_LOADER - идентификатор загрузчика списка улиц.

-   STORES_LOADER - идентификатор загрузчика списка магазинов.

-   CATEGORIES_LOADER - идентификатор загрузчика списка категорий.

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

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

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

-   SKIP_LOCATION_DIALOG_TAG - тег диалогового фрагмента, используемого для предоставления возможности пропустить этап получения координат.

Поля:

- Loader Manager - поле типа Loader Manager, содержащее ссылку на менеджер загрузчиков.

-   View Mapping - поле, содержащее массив идентификаторов типа int, задающих объекты для отображения данных в списке.

-   sendItem - поле типа Menu Item, содержащее ссылку на пункт меню для отправки снимка.

-   Send Button - поле типа Button, содержащее ссылку на кнопку отправки снимка.

- Progress Dialog - поле типа Progress Dialog Fragment, содержащее ссылку на диалоговый фрагмент, используемый для отображения прогресса выполнения задачи.

-   Report Dialog - поле типа Report Dialog Fragment, содержащее ссылку на диалоговый фрагмент, используемый для отображения информации об ошибках.

-   Try Again Dialog - поле типа Two Option Dialog Fragment, содержащее ссылку на диалоговый фрагмент, используемый для предоставления возможности пропустить этап получения координат.

-   Skip Location Dialog - поле типа Two Option Dialog Fragment, содержащее ссылку на диалоговый фрагмент, используемый для предоставления возможности пропустить этап получения координат.

- Has Problem Check Box - поле типа Check Box, содержащее ссылку на флажо, показывающий наличие проблем.

-   Comments Edit Text - поле типа Edit Text, содержащее ссылку на тестовое поле для ввода комментария.

-   Last FileId - поле типа String, содержащее идентификатор последнего загруженного снимка.

-   Successfully Saved - флаг типа boolean, указывающий на успешное сохранение снимка.

- preferences - поле типа Shared Preferences, содержащее ссылку на модуль настроек.

Методы:

- void try Begin Upload() - метод, осуществляющий валидацию пользовательского ввода перед операцией авторизации, а также отображающий результаты валидации.

-   void begin Upload() - метод, запускающий процесс асинхронной загрузки фото. В качестве завершающего метода используется on Activity Result.

-   void begin Get Location() - метод, запускающий процесс асинхронного получения координат. В качестве завершающего метода используется on Activity Result.

-   void begin Save (Location location) - метод, запускающий процесс асинхронного сохранения фото.

-   void end Save()- метод, вызываемый при окончании сохранения.

-   void setup Spinners() - метод, инициализацию выпадающих списков.

-   String get Spinner Value (String valueName) - метод, позволяющий получить текущее значение из выпадающего списка по имени колонки из таблицы БД, которая ассоциирована с определённым списком.

-   int getSpinner ValueId (Spinner spinner) - метод, позволяющий получить первичный ключ записи БД, значение которой выбрано в выпадающем списке.

-   void update Store Adapter() - метод, осуществляющий инициализацию выпадающего списка магазинов.

-   void update Street Adapter() - метод, осуществляющий инициализацию выпадающего списка улиц.

-   void update City Adapter() - метод, осуществляющий инициализацию выпадающего списка городов.

-   void update Categories Adapter() - метод, осуществляющий инициализацию выпадающего списка категорий.

-   void restore Spinner (Spinner spinner, Cursor cursor) - метод, разблокирующий выпадающий список для ввода.

-   void restore Selection (String field Name) - метод, восстанавливающий последнее выбранное значение для выпадающего списка.

-   Loader <Cursor> on Create Loader (int i, Bundle bundle) - метод интерфейса Loader Manager. Loader Callbacks <Cursor>, осуществляющий инициализацию загрузчков.

-   void on Load Finished (Loader<Cursor> cursor Loader, Cursor cursor) - метод интерфейса Loader Manager. Loader Callbacks<Cursor>, вызываемый при готовности загрузчика отдать данных.

-   void on Loader Reset (Loader<Cursor> cursor Loader) - метод интерфейса Loader Manager. Loader Callbacks <Cursor>, вызываемый при сбросе загрузчика.

-   Progress Dialog Fragment get Pro gress Dialog() - метод, возвращающий диалоговый фрагмент, используемый для отображения прогресса выполнения задачи.

-   Report Dialog Fragment set Report Dialog (int title Id) - метод, возвращающий диалоговый фрагмент, используемый для отображения информации об ошибках с заданным заголовком.

-   Report Dialog Fragment set Report Dialog (int titleId, int messageId) - метод, возвращающий диалоговый фрагмент, используемый для отображения информации об ошибках с заданным заголовком и телом сообщения.

-   Two Option Dialog Fragment get Try Again Dialog()- метод, возвращающий диалоговый фрагмент, используемый для предоставления возможности пропустить этап получения координат.

-   Two Option Dialog Fragment get Skip Location Dialog()- метод, возвращающий диалоговый фрагмент, используемый для предоставления возможности пропустить этап получения координат.

Класс WorkerService

Данный класс является наследником класса Inent Service и представляет собой сервис фоновой обработки операций со встроенной очередью обработки сообщений. Класс содержит набор строковых констант вида «ACTION _XXX», используемых со схожей целью, что и аналогичные целочисленные константы в классах-активити. Однако, если целочисленные константы используются для идентификации необходимого результата, то строковые константы используются для идентификации вызываемого метода.

Константы:

-   ACTION_UPLOAD_PICTURE - идентификатор интента, запускающего операцию загрузки фото.

-   ACTION_SAVE_PICTURE - идентификатор интента, запускающего операцию сохранения фото.

-   PENDING_INTENT_FIELD - ключ по которому доступен ожидающий интент для обратной связи с активити-инициатором.

-   SERVICE_ERROR - код ошибки, означающий ошибку сервиса. Может быть передан в вызывающую активити в метод on Activity Result в качестве параметра result Code.

-   UNKNOWN_HOST - код ошибки, означающий неверный адрес сервера. Может быть передан в вызывающую активити в качестве данных внутри интента.

-   HOST_NOT_SPECIFIED - код ошибки, означающий отсутствие связи. Может быть передан в вызывающую активити в качестве данных внутри интента.

-   IO_ERROR - код ошибки, означающий ошибку ввода/вывода. Может быть передан в вызывающую активити в качестве данных внутри интента.

-   SERVICE_UPLOADING - идентификатор нотификации в строке состояния при переходе сервиса в привилегированный режим.

Поля:

- Pending Intent - поле типа IWeb Client, содержащее ссылку на ожидающий интент для обратной связи с активити-инициатором.

-   Web Client - поле типа Pending Intent, содержащее ссылку на веб-клиент.

-   Host Name - поле типа String, содержащее имя сервера.

Методы:

- void on Create() - метод вызываемый после создание сервиса. Аналогичен конструктору, однако в отличие от него может общаться ко всем системным компонентам.

-   void on Handle Intent (Intent intent) - основной метод сервиса. Извлекает очередной интент из очереди сообщений и передаёт его на обработку сервису. В зависимости от идентификатора интента будет вызван один из описанных далее методов.

-   void logIn (Intent intent) - метод, осуществляющий операцию авторизации, а также, в случае успеха, запускающий обновление кэша.

-   void upload Picture() - метод, осуществляющий загрузку фото.

-   void savePicture (Intent intent) - метод, осуществляющий сохранение фото.

-   void send Pending (Intent intent) - метод, осуществляющий отправку ожидающего интента для обратной связи с активити-инициатором.

-   void repor tError(String message) - метод, осуществляющий отправку ожидающего интента с заданным сообщением об ошибке.

-   Notification get Upload Notification()- метод, создающий нотификацию необходимую для перехода сервиса в привилегированный режим.

3.3 Описание классов взаимодействия с данными

Одним из требований к реализуемому клиенту была минимизация используемого траффика. Для выполнения этого требования было решено реализовать кеширование данных на стороне клиента. Android SDK предоставляет возможность упрощённого создания и сопровождения базы данных, поэтому довольно удобно использовать простые реляционные структуры для сохранения информации на клиенте. В реализуемом дипломном проекте за кэширование данных отвечают классы из пакета data.

Класс Db Helper

Данный класс унаследован от абстрактного класса SQ Lite Open Helper, который предоставляет простой и удобный интерфейс для создания баз даны в ОС Android.

Константы:

- DB_NAME - константа, содержащая имя файла базы данных.

-   STORES_TABLE - константа, содержащая имя таблицы магазинов.

-   CATEGORIES_TABLE - константа, содержащая имя таблицы категорий.

-   DB_ID_FIELD - константа, содержащая имя служебного столбца с первичными ключами.

Статические поля:

- instance - поле типа, Db Helper содержащее синглтон-объект данного класса.

Методы:

- Db Helper getInstance (Context context) - статический метод доступа к синглтон-объекту.

-   void on Create (SQ Lite Database sq Lite Database) - метод базового класса SQLite Open Helper, вызываемый при создании базы данных. Объект sq Lite Database представляет собой абстракцию БД, и позволяет выполнять инициализирующие SQL-запросы.

-   void on Upgrade (SQ Lite Database db, int i, int i2) - метод базового класса SQ Lite Open Helper, вызываемый при увеличении версии БД. В данном проекте не используется.

-   void recreate Tables() - метод, позволяющий удалить текущие таблицы и создать новые. Используется для очистки кэша.

Класс Data Provider

Данный класс представляет собой локальную альтернативу полноценного поставщика данных. Класс предоставляет доступ к записям из таблиц БД в виде специальных объектов типа Cursor, позволяющих осуществлять итерацию по некоторому набору данных, являющимся результатом ассоциированного с курсором SQL-запроса.

Константы:

- STORES_URI - константа типа Uri, содержащая URI таблицы магазинов.

-   CATEGORIES_URI - константа типа Uri, содержащая URI таблицы категорий.

Поля:

- instance - поле типа, Data Provider содержащее синглтон-объект данного класса.

-   context - поле типа, Context содержащее ссылку на контекст приложения, необходимы для операций с БД.

Методы:

- Cursor get Categories() - метод, возвращающий объект типа Cursor для итерации по категориям.

-   Cursor get Streets (String city) - метод, возвращающий объект типа Cursor для итерации по всем улицам выбранного в параметре city города.

-   Cursor getStores (String city, String street) - метод, возвращающий объект типа Cursor для итерации по всем магазинам, выбранных в параметре city и street города и улицы.

-   void update Stores (List <StoreEntity> new Stores) - метод, осуществляющий обновление информации в таблице магазинов данными из списка newStores.

-   void update Categories (List <DbEntity> new Categories) - метод, осуществляющий обновление информации в таблице категорий данными из списка new Categories.

-   int get Last Version (String table Name) - метод, позволяющий получить последнюю версию указанной в параметре table Name таблицы.

-   int get Last Stores Version() - метод, позволяющий получить последнюю версию таблицы магазинов.

-   int get Last Categories Version() - метод, позволяющий получить последнюю версию таблицы категорий.

Описание вложенного пакета loaders

Данный пакет содержит в себе реализации классов-загрузчиков. Наиболее сложным элементом пакета является абстрактный класс Simple Cursor Loader, от которого унаследованы все используемые в проекте загрузчики. Он представляет собой типовую реализацию класса-загрузчика, предназначенного для работы с локальными объектами доступа к данным, наподобие класса Data Provider. Simple Cursor Loader по своей сути является слегка модифицированной версией класса Cursor Loader, являющегося частью Android SDK и ориентированного на работу с поставщиками данных. Поскольку задача реализации загрузчика, ориентированного на работу с локальной заменой поставщиков данных, является весьма распространённой, используемую в данном проекте реализацию Simple Cursor Loader можно считать типовой. Её описание можно найти, например, в литературном источнике [8].

Остальные классы-загрузчики являются наследниками класса Simple Cursor Loader и переопределяют метод Cursor get Cursor(), возвращающий курсор с актуальным содержимым, который, в свою очередь, возвращает один из методов класса доступа к данным - Data Provider. Помимо этого, некоторые классы-загрузчики содержат набор методов-установщиков для задания дополнительных данных ассоциированных с курсорами SQL-запросов, например, названия городов и/или улиц.

.4 Описание классов пакета dialogs

Данный пакет содержит определения классов, так или иначе наследуемых от класса Dialog Fragment и служащих для создания диалоговых окон. Как и в случае с пакетом loaders, данный пакет содержит базовый абстрактный класс Base Dialog Fragment, непосредственно унаследованный от класса Dialog Fragment, в то время как конкретные реализации диалоговых фрагментов унаследованы от класса Base Dialog Fragment.

Описание класса Base Dialog Fragment

Данный класс является базовым для всех остальных классов диалоговых фрагментов. Он унаследован от класса Dialog Fragment, оставляя метод on Create Dialog, непосредственно создающий диалоговое окно, для переопределения в классах потомках.

Поля:

- titleId - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве заголовка диалогового окна.

Методы:

- int get TitleId() - метод возвращающий текущее значение поля titleId.

-   void set TitleId (int titleId) - метод позволяющий установить новое значения для поля titleId.

-   void on Destroy View() - метод, определённый в базовом классе DialogFragment. Здесь переопределяется для обхода ошибки в Android SDK.

-   Dialog on Create Dialog (Bundle saved Instance State) - метод, определённый в базовом классе Dialog Fragment и являющийся в данном классе абстрактным, однако переопределённый во всех классах-потомках. Используется для создания диалогового окна, представленного наследником класса Dialog.

Описание класса Progress Dialog Fragment

Данный класс представляет собой диалоговый фрагмент, служащий для отображения бесконечного прогресса с заданным заголовком и текстом сообщения.

Поля:

- messageId - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве текста сообщения.

Методы:

- int get MessageId () - метод возвращающий текущее значение поля messageId.

-   void set MessageId (int messageId) - метод позволяющий установить новое значения для поля messageId.

Описание класса Redirect To Settings Dialog Fragment

Наиболее простой класс диалогового фрагмента. Схож с описанным ниже классом Report Dialog Fragment, однако использует строго определённые тексты заголовка и тела сообщения, а также не может быть закрыт с помощью кнопки «Назад».

Описание класса Report Dialog Fragment

Данный класс представляет собой диалоговое окно сообщения об ошибке с произвольным текстом заголовка и тела сообщения, а также единственной кнопкой «ОК». Важным моментом является то, что активити, использующая данный фрагмент должна реализовывать интерфейс Dialog Interface. On Click Listener для обработки нажатий по единственной кнопке диалога.

Поля:

- messageId - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве текста сообщения.

Методы:

- int get MessageId () - метод возвращающий текущее значение поля messageId.

-   void set MessageId (int messageId) - метод позволяющий установить новое значения для поля messageId.

-   void on Attach(Activity activity) - метод, определённый в базовом классе Dialog Fragment. Вызывается в момент ассоциации фрагмента с определённой активити, но до создания визуальных элементов. Используется для проверки реализации активити интерфейса Dialog Interface. On Click Listener.

Описание класса Report Dialog Fragment

Данный класс представляет собой диалоговое окно сообщения об ошибке с неизменяемым текстом заголовка и тела сообщения, а также двумя кнопками, содержащими произвольный, однако неизменяемый после создания диалога, текст. Все параметры задаются в конструкторе при создании фрагмента. Активити, использующая данный фрагмент, также, как и в случае с классом Report Dialog Fragment, должна реализовывать интерфейс Dialog Interface. On Click Listener для обработки нажатий кнопок диалога.

Поля:

- message Id - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве текста сообщения.

-   Positive Text Id - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве текста на позитивной кнопке диалога.

-   Negative Text Id - пол типа int, содержащее идентификатор строкового ресурса, используемого в качестве текста на негативной кнопке диалога.

Методы: void on Attach(Activity activity) - метод, определённый в базовом классе Dialog Fragment. Описан в классе Report Dialog Fragment и служащий для аналогичной цели.

3.5 Обзор функциональных классов из пакета helpers

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

Описание класса Accessibility Helper

Данный класс предоставляет набор методов для проверки доступности сети Интернет и статуса GPS на телефоне, а также предоставляет методы для создания диалоговых окон, перенаправляющих на соответствующие страницы системных настроек для включения необходимых функций.

- boolean is Networking Enabled (Context context) - метод, показывающий наличие интернет-соединения.

-   boolean is Gps Enabled(Context context) - метод, показывающий доступность провайдеров GPS-координат.

-   Alert Dialog get Dialog (final Context context, int message Id, final String action) - вспомогательный метод для создания диалога с произвольным сообщением, перенаправляющего на определённую страницу системных настроек.

-   Alert Dialog get Gps Dialog (final Context context) - метод, возвращающий диалог, перенаправляющий на страницу настроек интернет-соединения.

-   Alert Dialog get Network Dialog (final Context context) - метод, возвращающий диалог, перенаправляющий на страницу настроек GPS.

Описание класса Authorization Helper

Данный класс предоставляет функционал для авторизации на клиентской стороне, то есть обеспечения доступа согласно роли пользователя.

Константы:

- USER_ROLE - строковая константа, содержащая значение роли «пользователь».

-   MERCHANDISER_ROLE - строковая константа, содержащая значение роли «мерчендайзер».

-   AUTHORIZATION_ERROR - строковая константа-идентификатор, указывающая на ошибку авторизации.

Методы:

- boolean isValidRole(String role) - метод, показывающий наличие доступа к приложением пользователю с указанной в параметре role ролью.

Описание класса Image Store

Данный класс служит для передачи изображения в бинарном виде между различными активити. Необходимость для создания данного класса обусловлена тем, что интенты имеют ограничение на объём передаваемых данных, которое легко превысить, передавая изображение в качестве параметра интента.

Статические поля:

- image - фото в виде массива байт.

Статические методы:

- byte[] get Image() - возвращает изображение, сохранённое в поле image.

-   void store Image(byte[] image) - сохраняет изображение, в поле image.

-   void clean() - очищает поле image.

Описание класса Json Helper

Данный класс служит для упрощённого создания JSON объектов и используется классами взаимодействия с сетью. В отличие от остальных классов-помощников, данный класс обладает набором не только статическими методами. Класс имеет набор методов add с различной сигнатурой, являющимися полными аналогами методов put класса JSON Object, однако маскирующих неактуальное в данном проекте исключение JSON Exception.

Поля:

- json - поле типа JSONO bject, представляющее собой редактируемый JSON-объект.

Методы:

- Json Reader get Reader For Response (Http Response response) - статический метод, позволяющий получить потоковый JSON парсер типа Json Reader, ассоциированный с сетевым потоком из HTTP-ответа, переданного в качестве параметра response.

-   String Entity to String Entity() - метод, преобразующий получить редактируемый JSON-объект, хранящийся в поле json к объекту типа String Entity, который можно использовать в качестве тела HTTP-запроса.

Описание класса Location Helper

Данный класс используется для получения данных о текущем местоположении, а также, при тестировании, для эмуляции связанных с этим ошибок.

Константы:

- LATITUDE_FIELD - константа-ключ, по которому доступна широта в интенте с геоданными.

-   LONGITUDE_FIELD - константа-ключ, по которому доступна долгота в интенте с геоданными.

-   ACCURACY_FIELD - константа-ключ, по которому доступна точность в интенте с геоданными.

-   TIMEOUT - таймаут в милисекундах, после которого выдаётся ошибка о невозможности получения координат.

Поля:

- Error Reported - флаг типа boolean, предотвращающий повторную отправку интента с сообщением об ошибке.

Методы:

- void report Error(Context context, Pending Intent pending Intent) - метод осуществляющий отправку интента с сообщением об ошибке.

-   void get Location (final Context context, final Pending Intent pending Intent) - метод, позволяющий компоненту context асинхронно получить геоданные с помощью отложенного интента pending Intent.

3.6 Описание классов пакета networking

Данный пакет содержит элементы, необходимы для взаимодействия с сетью. Все классы пакета можно разделить на две группы: классы, представляющие собой структуры данных и веб-клиенты.

Классы, представляющие собой структуры данных реализуют ряд свойств в виде набора get- и set- методов для получения полей класса и установки их новых значений. Важной особенностью всех таких классов, является реализация интерфейса Parcelable, необходимого для пересылки экземпляров данных классов вместе с интентами.

Классы веб-клиенты реализуют интерфейс IWeb Client, описывающие программные эквиваленты веб-методов серверного API. При этом существует два класса веб-клиента: рабочий Web Client и тестовый Fаke Web Client, используемый для эмуляции работы сервера.

Описание класса Web Client

Данный класс представляет собой непосредственно клиент, реализующий методы веб-интерфейса, предоставляемого сервером и осуществляющий обмен данный по сети Интернет. Класс объявляет набор констант, названных по шаблону API_*, которые представляют собой названия веб-методов.

Поля:

- client - поле типа Default http Client, представляющее собой объект, занимающийся коммуникацией по протоколу HTTP.

-   Auth Header - строковое поле, содержащее заголовок авторизации.

-   Base Url - строковое поле, содержащее адрес сервера.

Методы:

- Http Response post (String method, Http Entity entity) - метод, отправляющий HTTP POST запрос по адресу веб-метода из параметра method с телом, заданным параметром entity.

-   String login (String login, String password) - метод, осуществляющий авторизацию с указанными логином и паролем и возвращающий роль пользователя.

-   List <Store Entity> update Stores (int version) - метод, позволяющий получить обновления таблицы магазинов выше версии переданной в параметре version в виде списка структур данных Store Entity.

-   List <Db Entity> update Categories (int version) - метод, позволяющий получить обновления таблицы категорий выше версии переданной в параметре version в виде списка структур данных DbEntity.

-   List <Store Entity> parse Stores List (Json Reader reader) - метод, позволяющий считать потоковым парсером reader массив элементов Store Entity.

-   List <DbEntity> parse Categories List (Json Reader reader) - метод, позволяющий считать потоковым парсером reader массив элементов Db Entity.

-   String parse Common Parameter (Db Entity entity, Json Reader reader) - метод, позволяющий извлечь из потока с данными в формате JSON, с помощью парсера reader, одно из полей объекта entity.

-   String upload Picture(byte[] picture) - метод загружающий фото в бинарном виде на сервер, возвращая определённый сервером GUID.

-   boolean send Metadata (Metadata data) - метод, позволяющий сохранить фото с указанными метаданными. Возвращаемое методом значение, показывает успешность выполнения операции.

4. Разработка программных модулей

Одной из важнейших частей приложения является класс Web Client, отвечающий за коммуникацию с сервером. Данный класс реализует интерфейс IWeb Client, описывающий серверное API в виде набора Java-методов. Особенностью этих методов является взаимодействие с сетью, поэтому они все они могут выбросить обязательное для обработки исключение IO Exception, а также не могу быть вызваны в основном потоке, поскольку это вызовет исключение Network On Main Thread Exception. Исходя из этого, методы, определённые в IWeb Client, должны вызываться в контексте, обеспечивающем выполнение в отличном от основного потоке и обрабатывающем исключения ввода/вывода. Таким контекстом является метод on Handle Intent класса Worker Service:

@Override

protected void on Handle Intent (Intent intent) {

try {(hostName != null && !hostName.isEmpty()) {action = intent.getAction();(intent.hasExtra(PENDING_INTENT_FIELD)) {= intent.getParcelableExtra(_INTENT_FIELD);

}(ACTION_LOGIN.equals(action)) {(intent);

} else if (ACTION_UPLOAD_PICTURE.(action)) {();

} else if (ACTION_SAVE_PICTURE.(action)) {(intent);

}

} else {(HOST_NOT_SPECIFIED);

}

} catch (UnknownHostException ex) {(UNKNOWN_HOST);

} catch (IOException ex) {.printStackTrace();(IO_ERROR);

}}

Метод on Handle Intent определён в базовом классе Intent Service и работает следующим образом: когда какой-либо компонент посылает сервису запрос с помощью интента, передаваемого в метод start Service, данный интент оборачивается в объект Message и добавляется в очередь обработки сообщений, ассоциированную с экземпляром класса Handler. Каждая очередь также ассоциирована с определённым потоком, в котором будет производится обработка сообщений. Если объект Handler определяет, что поток обработки простаивает, то он извлекает очередное сообщение (объект класса Message) из очереди и передаёт его в метод handle Message. Этот метод класса Handler не производит никакой обработки сообщения, поэтому для создания работоспособной очереди обработки используются наследники класса Handler. Класс Intent Service содержит ссылку на потомок класса Handler, который в методе handle Message извлекает интент из объекта Message и передаёт в метод on Handle Intent. Сам Intent Service, аналогично классу Handler, не обеспечивает никакой обработки переданных в on Handle Intent интентов, для чего также используются классы-наследники, коим и является класс Worker Service. Таким образом, метод on Handle Intent гарантированно вызывается в отличном от главного потоке, а блок catch с типом IO Exception обеспечивает обработку ошибок ввода/вывода.

В методе on Handle Intent нет прямых вызовов методов интерфейса IWeb Client, однако они имеются внутри методов log In, upload Picture и save Picture. Листинг этих методов приведён ниже вместе с листингом метода report Error, обеспечивающего информирование об ошибках, а также аналогичного ему метода send Pending, информирующего об успешном окончании операции.

private void reportError(String message) {{.send(this, SERVICE_ERROR, Intent().(CommonConstants.ERROR_FIELD, message));

} catch (PendingIntent.CanceledException e) {.printStackTrace();

}void sendPending(Intent intent) {{.send(this, Activity.RESULT_OK, intent);

} catch (PendingIntent.CanceledException e) {.printStackTrace();

}

Нетрудно заметить, что метод send Pending отличается от report Error тем, что использует код Activity.RESULT_OK и не передаёт никакой дополнительной информации об ошибках.

private void logIn(Intent intent) throws IOException {login = intent.getStringExtra(.LOGIN_FIELD);password = intent.getStringExtra(.PASSWORD_FIELD);role = webClient.logIn(login, password);(new Intent().(CommonConstants.ROLE_FIELD, role));(AuthorizationHelper.isValidRole(role)) {provider =

DataProvider.getInstance(this);.updateStores(webClient.updateStores(.getLastStoresVersion()));.updateCategories(webClient.(provider.

getLastCategoriesVersion()));

}

Данный метод инкапсулирует извлечение из интента необходимых параметров, а также осуществляет обновление кэша при успешной авторизации. Такое совмещение действий оказывается весьма удобным, если принять во внимание то, что, во-первых, метод logIn выполняется в фоновом потоке, и, следовательно, длительные и ресурсоёмкие операции с базой данных не будут занимать основной поток выполнения. Во-вторых, приложение не должно допускать пользователя к редактированию метаданных до тех пор, пока кэш находится в неактуальном состоянии. Таким образом, операции загрузки и сохранения фотографии гарантированно не будут извлечены из очереди обработки сообщений до тех пор, пока не закончится обновление БД.

private void uploadPicture() throws IOException {(SERVICE_UPLOADING,());[] picture = ImageStore.getImage();fileId = webClient.uploadPicture(picture);(true);(fileId != null) {(new Intent().putExtra(.FILE_ID_FIELD, fileId));;

}("Uploading error");

}

Метод upload Picture, осуществляющий загрузку фото на сервер, показывает сразу два характерных для Android-приложений решения. Во-первых, поскольку операция загрузки снимка занимает очень большое количество времени и при этом не должна прерываться, для её выполнения сервис входит в привилегированный режим с помощью вызова start Foreground. Это означает, что пользователь может спокойно свернуть приложение дожидаясь окончания загрузки, не опасаясь того, что ОС завершит приложение с целью экономии ресурсов. По завершению загрузки, сервис выходит из привилегированного режима используя метод stop Foreground. В качестве одного из параметров метод start Foreground принимает специальный объект-нотификацию, представляющую собой сообщение в строке состояния телефона, указывающее на то, что в данный момент происходит некоторая длительная высокоприоритетная операция. Метод stop Foreground, в свою очередь, принимает в качестве параметра флаг, указывающий на то, следует ли автоматически убрать нотификацию, созданную при входе в привилегированный режим или же пользователь должен будет сделать это сам.

Во-вторых, в метод upload Picture можно видеть, что изображение для загрузки передаётся не в качестве параметра интента, как это сделано с логином и паролем в методе logIn, а через статическое поле в классе Image Store. Это сделано для того, чтобы обойти ограничение, накладываемое интентом на размер своих параметров. Изображения зачастую превышают определённый для параметров лимит байт, что приводит к возникновению исключений во время выполнения программы.

private void savePicture(Intent intent) throws IOException {metadata = intent.getParcelableExtra(.METADATA_FIELD);result = webClient.sendMetadata(metadata);(!result) {("Save metadata error");

} else {.clean();(new Intent());

}

Метод save Picture, аналогично методу logIn, инкапсулирует извлечение данных из интента, однако если логин и пароль передавались как простые строковые параметры, то здесь используется сложная структура Metadata. Для обеспечения пересылки сложных структур данных можно воспользоваться предоставляемой Java бинарной сериализацией объектов, однако это не является эффективным способом конверси данных. Вместо этого, Android SDK предоставляет куда более гибкий и быстрый способ основанный на интерфейсе Parcelable. Каждый Parcelable-объект должен реализовывать метод void write To Parcel (Parcel parcel, int i), который вызывается каждый раз при преобразовании данных для пересылки. Объект parcel представляет собой очередь, способную хранить строки, примитивные типы и их массивы. Помимо этого, реализующие Parcelable классы должны содержать статическое поле CREATOR с объектом класса наследника типа Parcelable. Creator<Т>, где Т - тип, который содержит поле CREATOR. Инетрфейс Parcelable. Creator<Т> определяет метод Т create From Parcel (Parcel in), который вызывается на принимающей стороне. Этот метод извлекает из очереди in данные и создаёт из них копию пересылаемого Parcelable-объекта. Пример реализации интерфейса Parcelable представлен ниже с помощью фрагментов класса Metadata:

public class Metadata implements Parcelable {

<Объявление свойств и констант>

@Overridevoid writeToParcel(Parcel parcel, int i) {.writeInt(storeId)

< Запись остальных свойств в очередь parcel>

parcel.writeString(wasTaken);

}static final Parcelable.Creator<Metadata> CREATOR=Parcelable.Creator<Metadata>() {Metadata createFromParcel(Parcel in) {metadata = new Metadata();.storeId = in.readInt();

< Извлечение остальных свойств из очереди in>

metadata.wasTaken = in.readString();metadata;

}Metadata[] newArray(int size) { new Metadata[size];

}

В данном случае, объект CREATOR представлен анонимны классом, что упрощает инициализацию создаваемого им объекта класса Metadata.

Реализацию методов интерфейса I Web Client рассмотрим на примере метода logIn класса Web Client, листинг которого приведён ниже:

@OverrideString logIn(String login, String password)IOException {= "Basic "+ Base64.encodeToString(.format("%s:%s", login, password).(), Base64.NO_WRAP);response =(API_LOGIN, new JsonHelper().toStringEntity());(response != null) {status = response.().getStatusCode();(status == HttpStatus.SC_UNAUTHORIZED) {AuthorizationHelper.AUTHORIZATION_ERROR;

} else if (status == HttpStatus.SC_OK){reader =.getReaderForResponse(response);role = null;(reader != null) {{.beginObject();.nextName();= reader.nextString();.close();

} catch (IOException e) {.printStackTrace();

}role;

}response.getStatusLine().toString();

}"Error: Unable to get response";

}

Метод log In хорошо показывает особенности реализации веб-клиента в данном проекте. В нём можно выделить несколько основных элементов, также присутствующих во всех остальных методах веб-клиента:

- Вызов метода post. Данный метод формирует HTTP POST запрос с телом, переданным во втором параметре, и отправляет его по адресу веб-метода, указанного первым параметром. Листинг этого метода будет приведён далее по тексту.

-   Использование объекта Json Helper для формирования тела запроса. Остальные методы сначала использую несколько вызовов метода Json Helper.add для инициализации объекта, а затем приводят его к необходимому типу вызовом метода to String Entity, также приведённого ниже. Исключением является метод upload Picture, сразу использующий объект Byte Array Entity, поскольку телом запроса для этого метода не является JSON-объект.

-   Получение из HTTP-ответа потокового JSON-парсера, с помощью статического метода Json Helper. Get Reader For Response.

-   Последовательное извлечение данных из HTTP-потока с помощью ранее полученного JSON-парсера.

Листинг метода post:

HttpResponse post(String method, HttpEntity entity)IOException {request = new HttpPost(baseUrl + method);.setEntity(entity);.setHeader("Accept", "application/json");(entity instanceof StringEntity) {.setHeader("Content-type",

"application/json; charset=UTF-8");

}.setHeader("Authorization", authHeader); client.execute(request);

}

В методе осуществляется объединение базового адреса веб API с именем конкретного веб-метода, создание нового экземпляра HTTP POST запроса, установка его тела, а также HTTP-заголовков, содержащих тип содержимого, тип ответа и токен авторизации. В качестве возвращаемого значения выступает объект типа Http Response, представляющий собой HTTP-ответ на созданный HTTP POST запрос.

Логика методов to String Entity и get Reader For Response весьма прямолинейна, однако хорошо иллюстрирует типичные способы конвертации данных при взаимодействии с REST API в Android-приложениях:

public StringEntity toStringEntity() {{new StringEntity(.toString(), HTTP.UTF_8);

} catch (UnsupportedEncodingException e) {.printStackTrace();

}null;

}static JsonReader getReaderForResponse(response) {(response != null) {{new JsonReader(InputStreamReader(.getEntity().getContent()));

} catch (IOException e) {.printStackTrace();

}

}

return null;}

5. Программа и методика испытаний

Система тестирования Android предоставляет архитектуру и мощные инструменты для тестирования всех частей приложения на всех уровнях разработки (от отдельной части программы до приложения в целом). Система тестирования включает в себя следующие особенности:

.        Наборы тестов Android основаных на JUnit. Для тестирования класса, который не использует Android API, можно использовать обычные тесты из JUnit, или использовать расширения Android JUnit, для тестирования Android компонентов.

.        Android расширения JUnit предоставляют компоненто-ориентированные классы тестов. Эти классы предоставляют вспомогательные методы для создания mock-объектов и методов, которые помогают управлять жизненным циклом компонентов.

.        Наборы тестов содержатся в тестовых пакетах, которые похожи на основные пакеты программ

.        Инструменты SDK для создания и тестирования доступны в виде плагинов к IDE, а также в виде консольных приложений. Эти инструменты получают информацию из проекта тестируемого приложения и используют ее для автоматического создания build-файлов, файла манифеста и структуры каталогов для тестового пакета.

.        SDK также предоставляет monkeyrunner - API тестирования устройств с программами на Python, и UI/Application Exerciser Monkey (консольный инструмент для стресс-тестирования пользовательских интерфейсов путем отправки псевдослучайных событий на устройство.

В процессе работы над проектом компания-заказчик решила отказаться от полноценного тестирования в угоду ускорению процесса разработки, поэтому тестирование данного проекта является исключительно функциональным.

В основном тестирование производилось на смартфоне Samsung Galaxy S II с операционной системой Android 4.1.2 и разрешением экрана в 800х480 точек. Смартфон обладает всеми особенностями, необходимыми для полноценной работы всех возможностей приложения, поэтому дополнительно проводились тесты на других устройствах. В некоторых тестах использовался смартфон Huawei U8230 с установленной на него модифицированной версией ОС Android - CyanogenMod 9, однако должной вариативности это не принесло, хотя и позволило проверить отличное от 800х480 разрешение на реальном устройстве.

Для того, чтобы проверить работу приложения в различных условиях, использовался встроенный в SDK эмулятор ОС Android. Эмулятор позволяет создавать виртуальные устройства с практически любыми конфигурациями оборудования, однако не может эмулировать некоторые функции камеры, например, такие как вспышка и автофокус. Исходя из этого, эмулятор использовался в первую очередь для тестирования пользовательского интерфейса на различных разрешениях экрана. Для этого был создан один экземпляр предустановленной виртуальной машины «Nexus 4», обладающей характеристиками одноимённого смартфона, для которой, в последствии, менялось разрешения экрана, не затрагивая остальных технических параметров. Поскольку эмулятор, входящий в Android SDK обладает очень плохим быстродействием, побочным положительным эффектом такого тестирования явилась проверка на работоспособность приложения на «слабых» устройствах, не обладающих достаточно мощными аппаратными характеристиками.

Для того, чтобы решить проблему с быстродействием стандартного эмулятора, использующего микроархитектуру ARM для виртуальных машин, для тестов критических состояний, таких как обрыв сети или недоступность GPS, использовался сторонний эмулятор Genymotion. В отличие от стандартного эмулятора, Genymotion использует х86 архитектуру, что позволяет использовать технологии аппаратной виртуализации и в десятки раз ускоряет работу эмулятора. Помимо этого, Genymotion обладает значительно более удобной системой эмуляции местоположения. К недостаткам данного эмулятора можно отнести крайне нестабильную работу: Genymotion работает в паре с системой виртуализацииVirtual Box и при этом довольно часто теряет с ней соединение, что приводит к рестарту эмулятора. Также не редки и «тихие» вылеты на рабочий стол без выдачи каких-либо сообщений об ошибках. Несмотря на это, Genymotion является гораздо более удобным в работе, в основном благодаря невероятно высокой скорости работы, по сравнению со стандартным эмулятором. Стоит отметить, что поскольку Genymotion базируется на микроархитектуре х86, он не всегда в полной мере позволяет протестировать приложения, использующие нативные библиотеки, написанные на С/С++ и собранные с помощью Android NDK, поскольку, в их случае, такие библиотеки собираются отдельно под каждый конкретный тип архитектуры. Так как данный проект не использует таких библиотек, то эта особенность несущественна.

Говоря о программной поддержке тестирования приложения, стоит отметить несколько классов, которые использовались на различных этапах разработки проекта для обеспечения работоспособности в условиях не до конца готовой системы, а также для проверки экстремальных ситуаций на реальном устройстве. Ниже приведён частичный листинг класса FakeWebClient, использовавшийся в проекте для тестирования функциональности в то время как сервер был ещё не готов:

public class FakeWebClient implements IWebClient {

@OverrideString logIn(String login, String password) {"user";

}

@OverrideList<StoreEntity> updateCache(int version) {null;

}

@OverrideString uploadPicture(byte[] picture) {{out =FileOutputStream(String.format(

"/storage/extSdCard/X/screen%d.jpg",.getInstance().getTimeInMillis()));.write(picture);.close();

} catch (Exception e) {.printStackTrace();

}"some_random_generated_id";

}

@Overrideboolean sendMetadata(Metadata data) { true;

}

Данная версия вместо отправки фото на сервер, сохраняет его на SD-карту телефона для проверки качества получаемых фотографий.

Помимо нескольких классов, наподобие Fake Web Client, которые использовались для проверки остальных веб-методов, следует отметить класс Location Helper, содержащий несколько методов для симуляции ошибочных ситуаций. В качестве примера, ниже приведён листинг метода emulate Location Error, имитирующего помехи в работе GPS и возвращающего геокоординаты каждый третий запрос:

public static void emulateLocationError(Context context,PendingIntent pendingIntent) {++;(counter > 2) {= 0;(context, pendingIntent);;

}Looper myLooper = Looper.myLooper();Handler handler = new Handler(myLooper);.postDelayed(new Runnable() {void run() {(context, pendingIntent);

}

}, TIMEOUT - 3000);

Таким образом, несмотря на отсутствие unit- и monkey- тестов, в проекте были созданы условия для довольно информативного, хоть и не автоматизированного тестирования. Кроме этого итоговый продукт был протестирован на достаточно большом количестве конфигураций, как виртуальных, так и физических.

6. Руководство пользователя

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

Приложение не требовательно к ресурсам и способно работать на всех современных моделях телефонов. Единственным обязательным требованием является наличие задней камеры. Кроме этого, для того, чтобы воспользоваться всеми функциями приложения, требуется наличие GPS, а также камеры с автофокусом и вспышкой. Минимальная версия ОС Android - 4.0, «Ice Cream Sandwich».

При первом запуске приложение попросит указать адрес сервера для хранения фотографий (рисунок 6.1).

а) б)

Рисунок 6.1 - Первый запуск приложения: а - всплывающее окно; б - страница настроек

Помимо адреса сервера, страница настроек позволяет отменить получение координат при сохранении фотографии для ускорения загрузки.

После первоначального запуска, страница настроек доступна через контекстное меню на странице авторизации.

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

Рисунок 6.2 - Страница авторизации

При успешной авторизации, пользователь попадает на страницу взаимодействия с камерой (рисунок 6.3). При помощи кнопок в верхнем правом углу, пользователь может изменить разрешение снимка, а также установить режим вспышки. Кнопка в верхнем левом углу позволяет выйти из аккаунта, а кнопка в нижней части экрана - сделать снимок. После получения фото, в нижней части появляется панель с кнопками «Отмена» и «Далее», которая позволяет принять снимок или сделать повторный. После получения удовлетворительного изображения, следует нажать на кнопку «Далее», которая откроет страницу сохранения фото.

В верхней части экрана расположены несколько выпадающих списков для фильтрации списка магазинов. Первый список необходим для выбора города, после чего следующий за ним список улиц заполняется соответствующими значениями. После выбора улицы значениями заполняется третий по счёту выпадающий список с магазинами. Далее следует выбрать категорию товара из четвертого сверху списка, а затем заполнить опциональные поля: установить время снимка (до работы, после работы или прочерк), флажок наличия проблемы и дать произвольный комментарий.

а)

б) в)

Рисунок 6.3 -Камера: а - общий вид; б - меню выбор разрешения; в - подтверждение сохранения снимка

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

а) б)

в) г)

Рисунок 6.4 - Страница метаданых: а - общий вид; б - загрузка фото; в - получение координат; г - завершение сохранения

7. Технико-экономическое обоснование проекта Equation Section 7

.1 Описание проекта

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

На текущий момент компания-заказчик имеет широкую сеть торговых точек, визуальный контроль которых проблематичен, поскольку требует установки большого количества наблюдательного оборудования, а также увеличения штата сотрудников для работы с ним. Дополнительные проблемы могу создать и владельцы торговых точек, занимающихся перепродажей продукции, с которыми потребуется заключать соглашения по установке оборудования на их территории. С целью решения данной проблемы было решено разработать специальную систему, упрощающую ведение отчётности сотрудниками, а также позволяющую проводить визуальный контроль не только рядовыми сотрудниками, но и вышестоящими менеджерами. Система, будучи внедрённой в ежедневный рабочий процесс сотрудников, отслеживает проводимую ими работу, а также позволяет свести всю отчётность к произведению нескольких снимков с рабочего телефона. Система уменьшает излишний документооборот, тем самым снижая затраты на расходные материалы, а также уменьшая трудоёмкость составления отчётов. Кроме этого, система, позволяет с лёгкостью отслеживать рост прибыли и динамику различных показателей продаж в соотношении как с активностью каждого конкретного мерчендайзера, так и с влиянием тех или иных маркетинговых решений. В качестве побочной функции, система позволяет отслеживать эффективность работы отдельных мерчендайзеров, что может быть использовано кадровой службой предприятия для улучшения качественного показателя рабочего состава и. как следствие, более рационального расхода средств, отводимых на зарплату сотрудникам.

Таким образом, положительный экономический эффект у заказчика может быть получен вследствие:

1.      Уменьшения излишнего документооборота (снижения затрат на расходные материалы и уменьшения трудоёмкости составления отчётов).

2.      Мониторинга динамики продаж в зависимости от проводимых маркетинговых акций и мерчендайзиноговой политики.

.        Мониторинга работы сотрудников с целью улучшения качественного показателя кадрового состава.

7.2 Расчет сметы затрат и цены ПО

Разрабатываемый программный модуль относится к третьей категории сложности, поскольку не относится к ПО, планирующемуся к использованию под нагрузкой, либо в различных наукоёмких или требующих оптимизации системах. Однако, дополнительный коэффициент сложности выше единицы и равен 1,06 за счёт наличия интерактивного интерфейса. Программный модуль является ПО общего назначения и относится к категории новизны В (КН = 0,7). С большой долей уверенности можно сказать о наличии схожих систем у других компаний.

При разработке модуля используются существующие технологии и средства разработки, которые охватывают около 20 - 40% реализуемых функций, поэтому коэффициент использования стандартных модулей принимается равным 0,7.

Отправной точкой для расчёта плановой сметы затрат на разработку ПО, требуется определить общий объем программного продукта (VО). В качестве единицы измерения примем количество строк исходного кода (Lines of Code, LOC). Прогнозируемый общий объём ПО определяется по каталогу функций. Каталог функций данного программного модуля представлен в таблице 7.1.

Таблица 7.1 - Каталог функций ПО

Код функции

Наименование (содержание) функции

Объем функций (LOC)

101

Организация ввода информации

150

102

Контроль, предварительная обработка информации

450

109

Организация ввода/вывода информации в интерактивном режиме

320

201

Генерация структуры базы данных

4300

208

Организация поиска и поиск в базе данных

5480

305

Обработка файлов

720

506

Обработка ошибочых и сбойных ситуаций

410

507

Обеспечение интерфейса между компонентами

970

-

Общий объём (VО)

12800


На основе общего объёма и категории сложности ПО определяется нормативная трудоёмкость, которая, в данном случае, для VО = 12800 и третьей категории сложности, составит ТН = 260.

Исходя из нормативной трудоёмкости можно определить общую трудоёмкость ТО:

 

где КС - дополнительный коэффициент сложности;

КТ - коэффициент использования типовых программ и модулей;

КН - коэффициент новизны.

Подставив значения в формулу 7.1, получим:

Имея общую трудоёмкость, определяется численность исполнителей проекта, либо срок его разработки. Данный проект делался на заказ, при этом заранее было определено, что работа будет выполнена одним человеком, таким образом требуется определить второй параметр.

Для определения срока разработки проекта, необходимо рассчитать эффективный фонд времени одного работника (ФЭФ):

 

где ДГ - количество дней в году;

ДП - количество праздничных дней в году;

ДВ - количество выходных дней в году;

ДО - количество дней отпуска.

Таким образом, по формуле 7.2 фонд эффективного времени составит:

Срок разработки проекта (ТР) определяется по формуле:

 

где ЧР - численность исполнителей проекта;

ТО - общая трудоемкость разработки проекта;

ТР - срок разработки проекта;

ФЭФ - эффективный фонд времени работы одного работника.

Подставив значения в формулу 7.3, получим:


 

где ТСТ - трудоемкость изготовления ПО на данной стадии;

ТН - нормативная трудоемкость;

КС - коэффициент сложности изготовления ПО на выбранной стадии;СТ - удельный вес трудоемкости выбранной стадии разработки ПО;

КН - коэффициент новизны ПО на выбранной стадии.

При этом на «Рабочий проект» полученное значение трудоемкости изготовления ПО дополнительно умножается на КТ. Результаты расчетов трудоемкости по стадиям представлены в таблице 7.2.

Таблица 7.2 - Расчет трудоемкости разработки ПО с учётом стадий

Показатели

Стадии

Итого


ТЗ

ЭП

ТП

РП

ВН


Удельных веса трудоемкости стадии разработки (dСТ)

0,09

0,07

0,07

0,61

0,16

1,00

Коэффициент сложности (КС)

1,06

1,06

1,06

1,06

1,06

-

Коэффициент стандартных модулей (КТ)

-

-

-

0,8

-

-

Коэффициент новизны (КН)

0,7

0,7

0,7

0,7

0,7

-

Трудоемкость (ТСТ), чел./дн.

17,36

13,5

13,5

94,15

30,9

169,38


На основе уточнённого значения трудоёмкости заново рассчитаем срок разработки проекта используя формулу 7.3:

Основой для расчёта сметы затрат является основная заработная плата (ЗП) разработчиков проекта. В данном случае имеется один работник - инженер-программист 1-й категории (тарифный разряд - 12, тарифный коэффициент - 2,84). Месячная ставка исполнителя (ТМ) определяется через действующую ставку тарифного разряда и тарифный коэффициент:

 

где ТМ1 - месячная тарифная ставка первого разряда на момент составления сметы (конец 2013 года) в тысячах рублей;

ТК - тарифный коэффициент.

Месячная ставка, определённая по формуле 7.5, составит:

Исходя из месячной тарифной ставки рассчитывается часовая:

 

где ТЧ - часовая тарифная ставка (тыс. руб.);

ФР - среднемесячная норма рабочего времени в часах (170 часов).

При подстановке значений в формулу 7.6, получим:

Основная заработная плата исполнителей рассчитывается по формуле:

 

где n - количество исполнителей;

ТЧi - часовая тарифная ставка -го исполнителя (тыс. руб.);

ТЧ - количество часов работы в день, ч;

К - коэффициент премирования, принятый 1,4;

ФП - плановый фонд рабочего времени i-го исполнителя (дн.).

Учитывая число разработчиков n = 1, определим основную заработную плату по формуле 7.7:

Дополнительная заработная плата (ЗД) включает в себя оплаты отпусков и другие выплаты, предусмотренные законодательством, и определяется по формуле 7.8:

 

где НД - норматив дополнительной заработной платы (15%).

Отчисления в фонды социальной защиты и социального страхования определяются по следующим формулам:

 

 

где НСЗ - норматив отчислений в фонд социальной защиты наделения;

НСС - норматив отчислений в фонд социального страхования.

Отчисления в фонд социальной защиты - 34%, отчисления в фонд социального страхования - 0,6%. Исходя из этого, по формулам 7.9 и 7.10, получаем:

Расходы по статье «Материалы» отражают расходы на бумагу, тонер и прочие вещи, необходимые для разработки ПО. Нормы расхода материалов в суммарном выражении определяются в расчете на 100 строк исходного кода. Сумма затрат на расходные материалы определяется по формуле:

 

где НМ - норма расхода материалов в расчете на 100 строк исходного кода ПО равная 3% от ЗО, то есть 247 руб./100 строк;O - общий объем ПО (строк исходного кода).

Подставив значения в формулу 7.11, получим:

Расходы по статье «Машинное время» включает оплату машинного времени, необходимого для разработки и отладки ПО и определяются по формуле:

 

где НРМ - цена одного машино-часа (тыс. руб.);

ТО - общее время работы над проектом (часов).

Расходы по статье «Научные командировки» определяются по формуле:

 

где ННК - норматив расходов на командировки по организации (15%).

Расходы по статье «Прочие затраты» включают затраты на приобретение и подготовку специальной научно-технической информации и специальной литературы. Определяется по формуле:

 

где НПЗ - норматив прочих затрат в целом по организации.

Затраты по статье «Накладные расходы» (РН) связаны с необходимостью содержания аппарата управления, вспомогательных хозяйств и опытных производств. Определяются по формуле:

 

где НРН - норматив накладных расходов в целом по организации.

Общая сумма расходов по смете (СП) определяется как сумма выше рассчитанных показателей:

 

Подставив рассчитанные ранее значения в формулу 7.16, получим:

 

Прогнозируемая прибыль от создаваемого ПО определяется как:

 

где УР - уровень рентабельности ПО (15%).

На основе прогнозируемой прибыли определяется прогнозируемая цена ПО без налогов:

 

Подставляя значения в формулу 7.18, получим:

При расчёте отпускной цены дополнительно учитывается налог на добавочную стоимость (НДС):

 

где НДС - ставка НДС равная 20%.

По формуле 7.19 налог на добавочную стоимость равен:

Таким образом, с учётом НДС, отпускная цена рассчитывается как:

 

Подставив значения в формулу 7.20, получим:

В дополнение к выше рассчитанным параметрам, определяются расходы на сопровождение (РС) и освоение (РО) ПО:

 

 

где НС - норматив расходов на сопровождение ПО (20%);

НО - норматив расходов на освоение ПО (10%).

Используя формулы 7.21 и 7.22, определим значения расходов:

Все выше рассчитанные параметры, а также результирующие показатели сведены в таблицу 7.3.

Таблица 7.3 - Расчёт себестоимости и отпускной цены.

Наименование статьи

Норматив

Формула расчёта

Значение, тыс. руб.

Основная ЗП

-

8240,101


Дополнительная ЗП

НД =15%

1236,015


Отчисления в фонд соцзащиты

НСЗ =34%

3222


Отчисления в фонд соцстраха

НСС =0,6%

56,86


Материалы и комплектующие

НМ =247 руб/100 стр

31642


Машинное время

НРМ =2500 руб/ч

5239,4


Научные командировки

ННК =15%

1236,015


Прочие затраты

НПЗ =20%

1648,02


Накладные расходы

НРН =30%

2472,03


Общая сумма по смете

-

54992,32


Прогнозируемая прибыль

УР =25%

13748,08


Прогнозируемая цена без налогов

-

68740,4


НДС

НДС =20%

14540,37


Отпускная цена

-

82488,5


Сопровождение ПО

НС =20%

10998,5


Освоение ПО

НО =10%

5499,23



Учитывая налог на прибыль, можно рассчитать итоговую сумму, которая останется разработчику и будет является его экономическим эффектом:

 

где П - чистая прибыль;

ППС - прогнозируемая прибыль (тыс. руб.);

НП - налог на прибыль (24%).

Подставив значения в формулу 7.23, определим чистую прибыль:

Анализируя полученные данные, можно подвести итоги финансовой оценки проекта со стороны разработчика:

- себестоимость проекта составила 55 миллионов рублей;

-   прогнозируемая отпускная цена - 82,5 миллионов рублей;

-   чистая прибыль разработчика составит 10,5 миллионов рублей.

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

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

8. Обеспечение пожарной безопасности на ЗАО «Итранзишэн»

В настоящем разделе рассмотрим вопросы обеспечения пожарной безопасности на ЗАО «Итранзишэн». Пожарная безопасность на данном предприятии организуется посредствам функционирования двух систем: автоматизированной системы пожарной сигнализации (ПС) и системы оповещения о пожаре (СО).

ПС и СО представляют собой совокупность технических средств для обнаружения признаков пожара и информирования о возникновении пожара и/или необходимости и путях эвакуации.

Спроектированные системы ПС и СО предназначены для:

- обнаружения первичных факторов пожара (дым, тепло), в контролируемых помещениях здания;

-   отображения информации о работоспособности и неисправности установки;

-   формирования команд на включение системы оповещения о пожаре;

-   сообщения людям информации о возникновении пожара и путях эвакуации.

В качестве приёмно-контрольного оборудования используется оборудование производства «Ровалэнт».

Центральной частью системы являются приборы приёмно-контрольные охранно-пожарные «А16-512». Прибор предназначен для контроля 16 шлейфов пожарной сигнализации, с соответствующей индикацией состояния на выносной панели управления ВПУ-А16, и выдачи сигналов о пожаре на технические средства оповещения. К прибору подключаются модули расширения АР-16 (до 2-х шт.), каждый модуль расширения также осуществляет контроль 16-ти шлейфов сигнализации. Приборы А16-512, выносная панель ВПУ-А16 и модули расширения АР-16 объединяются в единую систему с помощью контроллера КСО-А по интерфейсу RS-485 Программирование системы, и управление осуществляется с помощью клавиатуры ВПУ-А16.

На предприятии ЗАО «Итранзишэн» установлены два прибора А16-512, объединённые в единую систему посредством контроллера КСО-А. Контроллер отвечает за организацию, сбор данных и отображение информации о состоянии системы, а также программирование конфигурации сети. Каждый из приборов А16-512 имеет по одному модулю расширения АР-16 (структура системы ПС приведена на рисунке 8.1).

Рисунок 8.1 - Структурная схема системы ПС

Каждый прибор осуществляет управление шлейфами пожарной сигнализации с включенными в них:

- тепловыми извещателями нормально-замкнутыми ИП 101-01 А2М;

-   дымовыми пожарными извещателями ИП212-5МУ;

-   ручными пожарными извещателями ИП5-2Р.

Шлейфы с дымовыми извещателями запрограммированы, как «Пожарный дымовой двухпроводный» со сбросом после верификации. В данном режиме, при срабатывании извещателя прибор с помощью реле сброса снимает напряжение в шлейфе (на 4 секунды), сбрасывая извещатель в состояние «Норма», и выдает извещение «Внимание». При повторном срабатывании любого извещателя в этом же шлейфе, в течение запрограммированного времени верификации, прибор выдает извещение «Пожар».

Тепловые шлейфы прибора запрограммированы как «Пожарный на 4 состояния». В данный шлейф включены пожарные извещатели нормально замкнутыми контактами (тепловые), к параллельным контактам которых установлены шунтирующие сопротивления 1.5 кОм. В данном режиме, при срабатывании извещателя прибор выдает извещение «Внимание» и ожидает в течение запрограммированного времени верификации срабатывания второго извещателя этого же шлейфа.

Релейный модуль РМ-64 устанавливается в контроллер КСО-А и служит для автоматического включения системы оповещения, передачи сигналов на УОО «Молния и ряда других функций. В проекте предусмотрено управление системой оповещении о пожаре в автоматическом режиме, в зависимости от сработавшего шлейфа, сигнализации и зоны срабатывания, а также управлением вентиляцией в случае пожара (отключение приточной и вытяжной вентиляции).

К выходам РМ-64, подключено устройство оповещения (УО) «Молния». УО предназначено для автоматического вызова спасательной пожарной бригады. После срабатывания УО у контролёра прибора А16-512 есть не более трёх минут для отмены вызова, в случае ложного срабатывания системы.

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

Оповещение о пожаре для людей, находящихся в здании, предусмотрено с учетом объемно-планировочных решений и конструктивных особенностей здания.

Выбор типа системы оповещения (СО) принят с учетом функционального назначения здания, и, в соответствии с правилами СНБ 2.02.02-01, используется система оповещения людей о пожаре типа СО-2 производства ОДО «Авангардспецмонтаж».

Прибор управления (ПУ) предназначен для построения систем управления оповещением и эвакуацией типа СО-2 и обеспечивает выполнение следующих основных функций:

- управление светозвуковыми, световыми оповещателями и указателями;

-   управление лампами аварийного освещения и устройствами разблокировки замков аварийных выходов;

-   прием сигналов «Пожар» от приборов приемно-контрольных пожарных (ППКП);

-   контроль исправности входных и выходных цепей;

-   контроль удаленных источников питания;

-   работу в ручном и автоматическом режиме.

ПУ предназначен для управления системами оповещения и эвакуации в шести независимых зонах. На предприятии выделено 4 зоны, соответствующие четырём этажам здания. В качестве оповещателей используются светозвуковые транспаранты АСТО-12С/1, световые транспаранты АСТО-12.

При поступлении сигнала «пожар» от реле прибора РМ-64 пожарной сигнализации, сначала производится включение оповещения и зоне, где непосредственно произошёл пожар. Потом с задержкой в 15 секунд производится оповещение в другую зону, расположенную на этом же этаже, и в то же время - на самый верхний этаж.

Прибор А16-512 установлен на первом этаже здания, на ресепшене. Извещатели установлены согласно требованиям ТКП 45-2.02-190-2010 «Пожарная автоматика зданий и сооружений. Строительные нормы проектирования». В каждом помещении установлено не менее двух извещателей. Ручные извещатели установлены на высоте менее человеческого роста, в легкодоступных местах на пути эвакуации.

Установка пожарной сигнализации является потребителем первой категории согласно ПУЭ и требует двух независимых источников питания: первый - от независимого щитка электропитания, а второй - от аккумуляторной батареи.

В качестве резервного источника электропитания системы оповещения применяется ИРПА124/1, который обеспечивает питание системы в дежурном режиме не менее суток, и в тревожном не менее часа.

Каждый из приборов А16-512 питается от аккумуляторной батареи ёмкостью 18А/ч, которая обеспечивает питание системы в дежурном режиме не менее суток и в тревожном - не менее трёх часов.

Все работы были произведены согласно правилам устройства электроустановок.

К обслуживанию установки допускаются лица, прошедшие инструктаж по технике безопасности. Происхождения инструктажа отмечается в соответствующем журнале.

Также инструктаж по пожарной безопасности производится со всеми сотрудниками в рамках инструктажа по технике безопасности. Сами инструктажи по ТБ производятся в соответствии с нормами охраны труда и отмечаются в специальном журнале.

Предприятие укомплектовано индивидуальными средствами пожаротушения с 20% переизбытком. Исходя из специфики предприятия, в основном используются порошковые огнетушители.

На каждом этаже вывешены планы эвакуации с указанием на них маршрутов эвакуации, а также расположением средств пожаротушения.

Все системы для обеспечения пожаробезопасности были установлены сторонним предприятием-подрядчиком и принимались непосредственно комиссией МЧС, которая также производит периодические проверки соблюдения норм пожаробезопасности.

Вышеописанная система, вкупе с организационными мероприятиями, обеспечивает достаточно высокий уровень пожаробезопасности на предприятии, а также позволяет осуществлять постоянный мониторинг за текущей обстановкой на объекте и своевременное реагирование на чрезвычайные ситуации.

Заключение

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

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

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

системный класс программный пакет

Список литературы

[1] Eckel, B. Thinking in Java (4th Edition) / Bruce Eckel - Prentice Hall Ptr, 2006. - 1079 с.

[2] Java™ Platform, Standard Edition 7 API Specification [Электронный ресурс]. - Электронные данные. - Режим доступа: http://docs.oracle.com/javase/7/docs/api/.

[3] Android Application Architecture. Part I: Background [Электронный ресурс]. - Электронные данные. - Режим доступа: http://vladnevzorov.com/2011/04/18/android-application-architecture-part-i-background/.

[4] Android Developers [Электронный ресурс]. - Электронные данные. - Режим доступа: http://developer.android.com/develop/index.html.

[5] Meier, R. Professional Android 2 Application Development / Reto Meier - WROX, 2010. - 576 с.

[6] Android Service - Tutorial [Электронный ресурс]. - Электронные данные. - Режим доступа: http://www.vogella.com/tutorials/AndroidServices/article.html.

[7] Учебник о Android. Уроки для начинающих Tutorial [Электронный ресурс]. - Электронные данные. - Режим доступа: http://startandroid.ru/ru/uroki/vse-uroki-spiskom.html.

[8] Android Design Patterns [Электронный ресурс]. - Электронные данные. - Режим доступа: http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html.

[9] Харди, Б. Программирование под Android / Б. Харди, Б. Филлипс - СпБ: Питер, 2014. - 592 с.

[10] Welcome to Android Studio [Электронный ресурс]. - Электронные данные. - Режим доступа: http://tools.android.com/welcome-to-android-studio

[11] Gradle [Электронный ресурс]. - Электронные данные. - Режим доступа: http://www.gradle.org/.

[12] Gradle Plugin User Guide [Электронный ресурс]. - Электронные данные. - Режим доступа: http://tools.android.com/tech-docs/new-build-system/user-guide.

[13] SQLite Documentation [Электронный ресурс]. - Электронные данные. - Режим доступа: http://sqlite.org/docs.html.

[14] Newest ‘android’ questions. Stackoverflow. [Электронный ресурс]. - Электронные данные. - Режим доступа: http://stackoverflow.com/questions/tagged/android.

Приложение А

Исходный текст класса LoginAtivity

public class LoginActivityActivityView.OnClickListener, TextWatcher,.OnEditorActionListener,.OnClickListener {static final int ACTION_CODE_LOGIN = 100;static final String SAVE_CREDENTIALS_PROPERTY = "skip_login";static final String PROGRESS_DIALOG_TAG = "progress";static final String REPORT_DIALOG_TAG = "report";static final String REDIRECT_DIALOG_TAG = "redirect";EditText loginEditText = null;EditText passwordEditText = null;CheckBox saveCredentialsCheckBox = null;Button signinButton = null;boolean saveCredetials = false;ProgressDialogFragment progressDialog;SharedPreferences preferences;

@Overridevoid onCreate(Bundle savedInstanceState) {.onCreate(savedInstanceState);(R.layout.activity_login);= (ProgressDialogFragment)getFragmentManager().(PROGRESS_DIALOG_TAG);= (EditText)findViewById(R.id.loginEditText);= (EditText)findViewById(R.id.passwordEditText);= (CheckBox)findViewById(R.id.saveCredentialsCheckBox);= (Button)findViewById(R.id.signinButton);.addTextChangedListener(this);.addTextChangedListener(this);.setOnEditorActionListener(this);.setOnClickListener(this);= PreferenceManager.getDefaultSharedPreferences(this);();

}

@Overrideboolean onCreateOptionsMenu(Menu menu) {.(1, 1, 1, R.string.action_settings).(R.drawable.ic_action_settings).(new Intent(this, SettingsActivity.class));super.onCreateOptionsMenu(menu);

}

@Overridevoid onClick(View view) {(view.getId()) {R.id.signinButton :();;

}void tryBeginAuthorizeUser() {(AccessibilityHelper.isNetworkingEnabled(this)) {(.getText().toString(),.getText().toString());

} else {.getNetworkDialog(this).show();

}

@Overridevoid onPause() {();.onPause();

}

@Overridevoid onActivityResult(int requestCode, int resultCode, Intent data) {(resultCode == RESULT_OK) {(requestCode) {ACTION_CODE_LOGIN :(data.getStringExtra(CommonConstants.ROLE_FIELD));;

}

} else {.dismiss();error = data.getStringExtra(CommonConstants.ERROR_FIELD);(WorkerService.HOST_NOT_SPECIFIED.equals(error)){();.setEnabled(true);

} else {(WorkerService.UNKNOWN_HOST.equals(error) ?.string.err_unknown_host : R.string.err_unknown);

}void navigateToCameraActivity() {intent = new Intent(this, CameraActivity.class);.setFlags(.FLAG_ACTIVITY_CLEAR_TOP|.FLAG_ACTIVITY_CLEAR_TASK|.FLAG_ACTIVITY_NEW_TASK);.dismiss();(intent);

}

//region TextWatcher implementation

@Overridevoid beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {

}

@Overridevoid onTextChanged(CharSequence charSequence, int i, int i2, int i3) {

}

@Overridevoid afterTextChanged(Editable editable) {.setEnabled(

!TextUtils.isEmpty(loginEditText.getText()) &&

!TextUtils.isEmpty(passwordEditText.getText()));

}

@Overrideboolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {(i == EditorInfo.IME_ACTION_DONE &&.isEnabled()) {inputManager =

(InputMethodManager)this.getSystemService(INPUT_METHOD_SERVICE);.hideSoftInputFromWindow(.getWindowToken(),.HIDE_NOT_ALWAYS);();

}true;

}

//endregionvoid beginAuthorizeUser(String login, String password) {.setEnabled(false);= new ProgressDialogFragment(.string.login_progress_title, R.string.login_progress_text);.show(getFragmentManager(), PROGRESS_DIALOG_TAG);pending = createPendingResult(_CODE_LOGIN,Intent(),.FLAG_ONE_SHOT);intent = new Intent(this, WorkerService.class).(CommonConstants.LOGIN_FIELD, login).(CommonConstants.PASSWORD_FIELD, password).(WorkerService.PENDING_INTENT_FIELD, pending);.setAction(WorkerService.ACTION_LOGIN);(intent);

}void endAuthorizeUser(String role) {(AuthorizationHelper.isValidRole(role)) {();

} else {(AuthorizationHelper.AUTHORIZATION_ERROR.equals(role) ?.string.err_invalid_credentials : R.string.err_unknown);

}

}void reportError(int messageId) {reportDialog = new ReportDialogFragment(R.string.error, messageId);.show(getFragmentManager(), REPORT_DIALOG_TAG);.dismiss();.setEnabled(true);

}void showRedirectDialog() {RedirectToSettingsDialogFragment()

.show(getFragmentManager(), REDIRECT_DIALOG_TAG);

}

@Overridevoid onClick(DialogInterface dialogInterface, int i) {.dismiss();

}

//region Work with preferencesvoid restoreProperties() {= preferences.getBoolean(SAVE_CREDENTIALS_PROPERTY, false);.setChecked(saveCredetials);(saveCredetials) {login = preferences.getString(CommonConstants.LOGIN_FIELD, "");password = preferences.getString(CommonConstants.PASSWORD_FIELD, "");.setText(login);.setText(password);

}hostName = PreferenceManager.(this).(CommonConstants.HOST_NAME_PROPERTY, null);(hostName == null || hostName.isEmpty()) {();

}void savePreferences() {= saveCredentialsCheckBox.isEnabled();.Editor editor = preferences.edit();.putBoolean(SAVE_CREDENTIALS_PROPERTY, saveCredetials);(saveCredetials) {.putString(CommonConstants.LOGIN_FIELD, loginEditText.getText().toString());.putString(CommonConstants.PASSWORD_FIELD, passwordEditText.getText().toString());

}

editor.commit();

}

Приложение Б

Исходный текст класса CameraActivityclass CameraActivityActivitySurfaceHolder.Callback,.PictureCallback,.OnClickListener,.AutoFocusCallback,.OnItemClickListener{static String PROPERTY_SIZE_INDEX = "PictureSizeIndex";static String PROPERTY_FLASH = "Flash";Camera camera;int cameraId;LinearLayout bottomPanel;RelativeLayout cameraButtonLayout;Button cameraButton;OrientationEventListener orientationObserver;int iconOrientation = Surface.ROTATION_0;Camera.Size[] pictureSizes;int pictureSizeIndex = 0;SharedPreferences preferences;boolean useAutoFocus = false;boolean useFlash = false;boolean hasFlash = false;ActionBar actionBar = null;Button settingsButton;ImageView settingsImageView;Button logOutButton;ImageView logOutImageView;Button flashButton;ImageView flashImageView;ImageView cameraIconImage = null;PopupWindow popupWindow = null;int popupWindowCenter = 0;SurfaceHolder surfaceHolder = null;

//region Activity lifecycle

@Overridevoid onCreate(Bundle savedInstanceState) {.onCreate(savedInstanceState);(R.layout.activity_camera);.clean();= tryGetBackCamera();= getPictureSizes(camera);= PreferenceManager.getDefaultSharedPreferences(this);= preferences.getInt(PROPERTY_SIZE_INDEX, 0);= getFlashAvailability();= hasFlash && preferences.getBoolean(PROPERTY_FLASH, false);= ((SurfaceView)findViewById(R.id.cameraSurface)).getHolder();surfaceHolder != null;.addCallback(this);= getActionBar();actionBar != null;.setDisplayShowTitleEnabled(false);.setDisplayUseLogoEnabled(false);.setDisplayShowHomeEnabled(false);.setDisplayShowCustomEnabled(true);(R.id.cameraButton).setOnClickListener(this);(R.id.cancelButton).setOnClickListener(this);(R.id.continueButton).setOnClickListener(this);= (LinearLayout)findViewById(R.id.bottomPanel);= (Button)findViewById(R.id.cameraButton);= (ImageView)findViewById(R.id.cameraIconImage);= (RelativeLayout)findViewById(R.id.cameraButtonLayout);

}void onPause() {.disable();();.edit().(PROPERTY_SIZE_INDEX, pictureSizeIndex).(PROPERTY_FLASH, useFlash).();(!bottomPanelHidden) {();

}.onPause();

}

@Overridevoid onResume() {();();(settingsButton != null) {.enable();

}(camera == null) {= tryGetBackCamera();

}();(surfaceHolder);.startPreview();.onResume();

}

//endregion

//region Methods for cameraCamera tryGetBackCamera() {(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {= findBackFacingCamera();(cameraId >= 0) {Camera.open(cameraId);

}null;

}int findBackFacingCamera() {cameraId = -1;numberOfCameras = Camera.getNumberOfCameras();(int i = 0; i < numberOfCameras; i++) {.CameraInfo info = new Camera.CameraInfo();.getCameraInfo(i, info);(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {= i;;

}cameraId;

}void closeCamera() {(camera != null) {.release();= null;

}int getRotationValue() {rotation = this.getWindowManager().getDefaultDisplay().getRotation();degrees = 0;(rotation) {Surface.ROTATION_0: degrees = 0; break;Surface.ROTATION_90: degrees = 90; break;Surface.ROTATION_180: degrees = 180; break;Surface.ROTATION_270: degrees = 270; break;

}.CameraInfo info = new Camera.CameraInfo();.getCameraInfo(cameraId, info);(info.orientation - degrees + 360) % 360;

}boolean getFlashAvailability() {

//noinspection ConstantConditions(camera != null && getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {<String> modes = camera.getParameters().getSupportedFlashModes();modes != null && // workaround for some tablets an chinese devices

!(modes.size() == 1 &&.Parameters.FLASH_MODE_OFF.equals(modes.get(0)));

}false;

}Camera.Size[] getPictureSizes(Camera camera) {<Camera.Size> sizes = camera.getParameters().getSupportedPictureSizes();sizes != null;count = sizes.size();(count > 3) {= sizes.subList(count - 3, count);

}sizes.toArray(new Camera.Size[sizes.size()]);

}void setPictureSize(int index) {.Size newSize = pictureSizes[index];= index;.Parameters parameters = camera.getParameters();.setPictureSize(newSize.width, newSize.height);.setParameters(parameters);

}void setFlashMode(boolean useFlash) {.useFlash = useFlash;.Parameters parameters = camera.getParameters();.setFlashMode(useFlash ?.Parameters.FLASH_MODE_ON : Camera.Parameters.FLASH_MODE_OFF);.setParameters(parameters);

}void setUpCameraParameters() {.Size newSize = pictureSizes[pictureSizeIndex];.Parameters parameters = camera.getParameters();<String> focusModes = parameters.getSupportedFocusModes();focusModes != null;(focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);= true;

} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);

}.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);.setExposureCompensation(0);.setPictureSize(newSize.width, newSize.height);(hasFlash) {.setFlashMode(useFlash ?.Parameters.FLASH_MODE_ON : Camera.Parameters.FLASH_MODE_OFF);

}.setParameters(parameters);

}void resetCameraPreview(SurfaceHolder surfaceHolder) {{.setDisplayOrientation(getRotationValue());

//setCameraRotation(getRotationValue());.setPreviewDisplay(surfaceHolder);

} catch (IOException exception) {();

//endregion

//region Options menu

@Overrideboolean onCreateOptionsMenu(Menu menu) {inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);view = inflater.inflate(R.layout.menu_camera, null);.setCustomView(view);= (Button)view.findViewById(R.id.logoutButton);= (ImageView)view.findViewById(R.id.logoutImageView);= (Button)view.findViewById(R.id.flashButton);= (ImageView)view.findViewById(R.id.flashImageView);= (Button)view.findViewById(R.id.settingsButton);= (ImageView)view.findViewById(R.id.settingsImageView);.setOnClickListener(this);.setOnClickListener(this);(hasFlash) {.setOnClickListener(this);

} else {.setEnabled(false);.setAlpha(0.7f);

}();.enable();true;

}

//endregion

//region Click listener callbacks

@Overridevoid onClick(View view) {(view.getId()) {R.id.cameraButton :(camera != null) {.setEnabled(false);(useAutoFocus) {.autoFocus(this);

} else {.takePicture(null, null, this);

}

};R.id.cancelButton:.clean();();(camera != null) {.startPreview();

};R.id.continueButton:();;R.id.logoutButton:();;R.id.flashButton:(!useFlash);();;R.id.settingsButton:(popupWindow.isShowing()) {.dismiss();

} else {.showAsDropDown(settingsButton);

};

}

@Overridevoid onItemClick(AdapterView<?> adapterView, View view, int i, long l) {(i);

}

//endregion

//region Navigationvoid navigateToLoginActivity() {intent = new Intent(this, LoginActivity.class);.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);(intent);

}void navigateToDataActivity() {intent = new Intent(this, DataActivity.class);();(intent);

}

//endregion

//region SurfaceHolder.Callback implementation

@Overridevoid surfaceCreated(SurfaceHolder surfaceHolder) {(surfaceHolder);

}

@Overridevoid surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {.startPreview();

}

@Overridevoid surfaceDestroyed(SurfaceHolder surfaceHolder) {();

}

//endregion

//region Camera callbacks

@Overridevoid onPictureTaken(byte[] bytes, Camera camera) {.storeImage(correctImage(bytes));();

}void onAutoFocus(boolean b, Camera camera) {.takePicture(null, null, this);

}

//endregionbyte[] correctImage(byte[] data) {{realImage = BitmapFactory.decodeByteArray(data, 0, data.length);bitmap = rotateImage(realImage, Math.abs(iconOrientation - 90));stream = new ByteArrayOutputStream();.compress(Bitmap.CompressFormat.JPEG, 100, stream);[] newData = stream.toByteArray();.close();.d("Camera", String.format("Image size: %d", newData.length));newData;

} catch (IOException e) {.printStackTrace();

}null;

}Bitmap rotateImage(Bitmap bitmap, int degree) {width = bitmap.getWidth();height = bitmap.getHeight();matrix = new Matrix();.postRotate(degree);Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

}

//region Visual state managementboolean bottomPanelHidden = true;void swapElementsVisibility() {= !bottomPanelHidden;.setEnabled(bottomPanelHidden);.setEnabled(bottomPanelHidden);.setEnabled(bottomPanelHidden);(bottomPanelHidden) {.setVisibility(View.GONE);.setVisibility(View.VISIBLE);

} else {.setVisibility(View.VISIBLE);.setVisibility(View.GONE);

}void rotatePopup(int toOrientation) {(popupWindow != null) {needsToShow = popupWindow.isShowing();.dismiss();view = popupWindow.getContentView();.setPivotY(popupWindowCenter);.setPivotX(popupWindowCenter);.setRotation(toOrientation);(needsToShow) {.showAsDropDown(settingsButton);

}void tryStartRotation(View view, int toOrientation) {(view != null) {animation = getAnimation(toOrientation); // New animation object is needed to restore animation's center point.startAnimation(animation);

}void rotateImages(int toOrientation) {(logOutImageView, toOrientation);(flashImageView, toOrientation);(settingsImageView, toOrientation);(cameraIconImage, toOrientation);

}RotateAnimation getAnimation(int toOrientation) {animation = new RotateAnimation(, toOrientation,.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);.setFillAfter(true);.setDuration(600);animation;

}void updateFlashIcon() {(useFlash) {.setImageResource(R.drawable.ic_action_flash_on);.makeText(this, R.string.toast_flash_on, Toast.LENGTH_SHORT).show();

} else {.setImageResource(R.drawable.ic_action_flash_off);.makeText(this, R.string.toast_flash_off, Toast.LENGTH_SHORT).show();

}

//endregion

//region Setup methodsvoid setUpOrientationEventListener() {(orientationObserver == null) {= new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {

@Overridevoid onOrientationChanged(int i) {newOrientation = -100;(i >= 315 || i < 45) {= 0;

} else if (i >= 45 && i < 135) {= -90;

} else if (i >= 225 && i < 315) {= 90;

}(newOrientation > -100 && iconOrientation != newOrientation) {(newOrientation);(newOrientation);= newOrientation;

}PopupWindow setUpPopupWindow() {(popupWindow == null) {layoutInflater = (LayoutInflater)getBaseContext()

.getSystemService(LAYOUT_INFLATER_SERVICE);popupView = layoutInflater.inflate(R.layout.menu_camera_popup, null);popupView != null;list = (ListView)popupView.findViewById(R.id.qualitiesListView);dummy = (TextView)popupView.findViewById(R.id.dummyTextView);<String> humanReadableHeaders = getHumanReadableHeaders();(list, humanReadableHeaders);.setOnItemClickListener(this);.setItemChecked(pictureSizeIndex, true);

// Measure is need to set PopupWindow width similar to WRAP_CONTENT behaviour

// Unfortunately this property of popupView is ignored and without this fix

// PopupWindow's width is equal to screen width..measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);side = popupView.getMeasuredWidth();= side / 2;= getPopupWindow(popupView, side, side);.setOnClickListener(new View.OnClickListener() {

@Overridevoid onClick(View view) {.dismiss();

}popupWindow;

}ArrayList<String> getHumanReadableHeaders() {[] headers = this.getResources().getStringArray(R.array.quality_headers);<String> humanReadableHeaders = new ArrayList<String>();sizesCount = pictureSizes.length;(int i = 0; i < 3; i++) {(i < sizesCount) {.Size size = pictureSizes[i];.add(.format("%s (%dx%d)", headers[i], size.width, size.height));

} else {;

}humanReadableHeaders;

}void setPopupListContent(ListView list, final ArrayList<String> humanReadableHeaders) {array = getTheme().obtainStyledAttributes(int[]{android.R.attr.listPreferredItemHeightSmall});array != null;int height = array.getDimensionPixelSize(0, -1);.recycle();

// Extended adapter is needed to set equal width to all items in pop up list.

// It's supposed that the first row is the widest one (and it's true).

// It's obvious that such solution can cause bugs with list item's size.

// Unfortunately there is no other way to simulate action bar drop down behaviour..setAdapter(ArrayAdapter<String>(this,.layout.quality_list_item,.id.headerTextView,) {int maxWidth = 0;

@OverrideView getView(int position, View convertView, ViewGroup parent) {view = super.getView(position, convertView, parent);view != null;(position == 0) {.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);= view.getMeasuredWidth();

} else {.setLayoutParams(new ListView.LayoutParams(maxWidth, height));

}view;

}PopupWindow getPopupWindow(View view, int width, int height) {popupWindow = new PopupWindow(view, width, height, true);

//noinspection deprecation.setBackgroundDrawable(new BitmapDrawable());.setTouchable(true);.setOutsideTouchable(true); popupWindow;

}

//endregion

}

Похожие работы на - Мобильное приложение для оценки эффективности мерчендайзинга торговой компании

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!