четверг, 22 ноября 2012 г.

Синтез голоса в Android-приложении

Когда мы делаем наши Android-приложения, часто приходится сообщать что-то пользователю. Мы открываем ему AlertDialog, показываем Toast, бросаем сообщения в панель уведомления, сигналим, вибрируем... Вершина этой пирамиды уведомлений - просто сказать то что хочешь от клиента "человеческим голосом".
Голос, само собой, будет не совсем человеческим. Мы его синтезируем. То есть, Google синтезирует, а мы его об этом вежливо попросим.
Конечно, это не единственный спрособ: есть десяток голосовых движков, которые замечательно обходятся без Гугла, но подключать их в своё приложение - отдельная непростая (а порой и недешёвая) задача. Да, для них не нужен Интернет, но если ваше приложение и так его требует, то зачем себя исскуственно ограничивать? ;)

пятница, 26 октября 2012 г.

PreferenceActivity: Простое решение для настроек в Android-приложении

Итак, вы потратили пару недель и массу усилий, чтобы сделать своё Android-приложение. Пора бы уже и публиковать, но тут вспоминаем что не хватает одной мелочи: клиенту негде редактировать свои настройки. Верстать ещё одну форму? Не хочется, заказчик забыл согласовать дизайн да и сроки уже поджимают. Вот бы сделать что-то стандартное, простое с минимумом кода! И ведь это вполне возможно. Просто берём и используем PreferenceActivity.

1. Вот оно, PreferenceActivity:

import android.os.Bundle;
import android.preference.PreferenceActivity;
 
public class PrefsActivity extends PreferenceActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.prefs);
    }
}

Совсем просто, не правда ли? Конечно, вся простота опирается на хитрую xml-разметку в файле res/xml/prefs.xml:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
    <PreferenceCategory
            android:summary="Username and password information"
            android:title="Login information" >
        <EditTextPreference
                android:key="username"
                android:summary="Please enter your login username"
                android:title="Username" />
        <EditTextPreference
                android:key="password"
                android:summary="Enter your password"
                android:title="Password" />
    </PreferenceCategory>
 
    <PreferenceCategory
            android:summary="Login and UI settings"
            android:title="Settings" >
        <CheckBoxPreference
                android:key="checkBox"
                android:summary="On/Off"
                android:title="Keep me logged in" />
 
        <ListPreference
                android:entries="@array/listOptions"
                android:entryValues="@array/listValues"
                android:key="listpref"
                android:summary="Role"
                android:title="You role" />
    </PreferenceCategory>
</PreferenceScreen>

Тут описаны три элемента для ввода данных в двух категориях. Два EditTextPreference, CheckBoxPreference и ListPreference. Первые по клику пользователя покажут поля для ввода, второй и третий соответственно галочку и список. Для списка нужно описать варианты для выбора. Это делаем в файле res/values/array.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="listOptions">
        <item>Customer</item>
        <item>Freelancer</item>
        <item>Manager</item>
    </string-array>
 
    <string-array name="listValues">
        <item>You are customer</item>
        <item>You are freelancer</item>
        <item>You are manager</item>
    </string-array>
</resources>

Два массива содержат соответственно то что увидит пользователь в списке выбора и то, что запишется в настройки. А в какие ключи SharedPreferences будут сохранены данные, спросите вы? Присмотритесь-ка к первому из xml приведённых тут. У каждого элемента есть атрибут android:key. Это и есть тот ключ, в который система сохранит настройки. Заметьте, без всякого нашего участия.

2. Наслаждаемся результатом:

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

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
public class MyActivity extends Activity {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
 
    public void setPrefs(View v) {
        Intent intent = new Intent(this, PrefsActivity.class);
        startActivity(intent);
    }
 
    public void showPrefs(View v) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String username = prefs.getString("username", "Anonymous");
        String passw = prefs.getString("password", "Not set");
        boolean checkBox = prefs.getBoolean("checkBox", false);
        String listPrefs = prefs.getString("listpref", "Not set");
        StringBuilder builder = new StringBuilder();
        builder.append("Username: " + username + "n");
        builder.append("Password: " + passw + "n");
        builder.append("Keep me logged in: " + String.valueOf(checkBox) + "n");
        builder.append("Role: " + listPrefs);
        ((TextView) findViewById(R.id.prefs)).setText(builder.toString());
    }
}

Разметка для него:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:orientation="vertical" android:gravity="center_horizontal" android:paddingTop="30dp">
 
    <Button
            android:id="@+id/btnSetPrefs"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Edit Preferences" android:onClick="setPrefs"/>
 
    <Button
            android:id="@+id/btnShowPreferences"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Display preferences" android:onClick="showPrefs"/>
 
    <TextView
            android:id="@+id/prefs"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
 
</LinearLayout>

И результат, после перехода к настройкам, ввода их, возврата и нажатия на кнопку Display preferences:



вторник, 31 июля 2012 г.

PhoneGap: используем чужие плагины и пишем свои

Предыдущий мой пост о PhoneGap был рассчитан на начинающих разработчиков для мобильных устройств. Теперь давайте обсудим, какую пользу из этого фреймворка могут извлечь те, кто уже умеет делать "настоящие" Android (IOS и т.п) приложения.
Если вам приходилось работать над сравнительно большим приложением в тесном контакте с заказчиком, то вы знаете, какой процент требований заказчик выставляет "вдогонку", после утверждения ТЗ. Можно долго обсуждать, насколько это неприятно и неправильно, но с этим, как правило, приходиться жить. Большинство таких "дополнительных" требований касается того, что заказчик видит, т.е. интерфейса: "Тут сделайте жирным, а вот это подвиньте...". Здорово было бы, чтобы эту малоприятную работу делали не суровые java(objective-C)-программисты, а обычные html-css верстальщики, которых, кстати, нанять легче и дешевле. Таким образом приложение с помощью PhoneGap можно сделать не только кроссплатформенным, но и "двухслойным". Интерфейс и навигацию делаем на HTML5, а "тяжёлую" логику, если она есть, делаем нативной. При этом основной разработчик освобождается от вёрстки xml layout-ов, что само по себе уже даёт существенный прирост его производительности.
На выходе получаем вполне здоровый производственный процесс: html-css-javacsript-программисты пишут приложение на PhoneGap, а за всякой нетривиальной задачей обращаются к плагинам, которые пишут "настоящие" Android-разработчики. Вот давайте и посмотрим, насколько легко можно писать и использовать плагины для PhoneGap.

суббота, 28 июля 2012 г.

Поддержка управления в Android с помощью жестов.

Жесты - один из важнейших способов ввода в Android устройствах. Существует несколько категорий жестов, основные из которых это SingleTouch и Multitouch. 

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

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

Основой для приложения будет класс ScaleGestureDetector,  основной задачей которого является "узнавание" жестов масштабирования, и уведомления приложения с помощью CallBack'ов. При его использовании необходимо учитывать, что данная возможность доступна с версии Android 2.0 (Froyo).

Начнем с создания собственного ImageView.

четверг, 26 июля 2012 г.

PhoneGap: пишем для Android на Html и JavaScript


Разработка для мобильных платформ становится всё популярнее, затягивая всё больше программистов из смежных отраслей. Если вы писали, к примеру, серверный Java-код, то проблем с освоением платформы Android у вас скорее всего не будет. А если "в прошлой жизни" вы делали сайты на html+css+javascript? Тут нужно не только учить новый язык, но и осваивать "настоящее" ООП, которое не сразу и не каждому даётся. Если же вы решили охватить и IPhone/IPad, то учить нужно ещё и Objective-C. Что же делать, если учить новые технологии некогда, а подарить миру своё приложение для смартфонов хочется уже сейчас?
Фреймворк PhoneGap помогает решить эту проблему. Пишите приложение как локальную веб-страницу, а доступ к возможностям смартфона предоставит PhoneGap. Тут я постараюсь дать пошаговое руководство, как сделать простое Android-приложение на Html и JavaScript с помощью PhoneGap. Писать будем в IntelliJ IDEA. Эта замечательная IDE в последнее время сумела выбросить Eclipse из моего компьютера :)
Итак, приступим. Тут будет много скриншотов: лучше один раз увидеть, чем один раз прочитать.

среда, 25 июля 2012 г.

Используем фрагменты в приложении для Android 2.х

Разработчики Android основательно поработали над тем, чтобы наше приложение нормально выглядело как на десятидюймовом планшете, так и на смартфоне с экраном в 3,5 дюйма. Мы можем использовать несколько наборов графики, разные варианты разметки интерфейса в зависимости от ориентации экрана. И компоновать эти интерфейсы мы можем из произвольного набора автономных фрагментов. Конечно, фрагменты поддерживаются начиная с четвёртой версии Android, но с помощью android 4 support library и небольшого танца с бубном, можно реализовать эту технологию и под Android 2.х.
Давайте сделаем небольшое приложение "на фрагментах".

пятница, 20 июля 2012 г.

Распознаём QR-код в Android-приложении

Ранее я писал о том, как сгенерировать QR-код в Android-приложении. Для полноты картины не хватает ещё примера, как распознать QR-код. Простое решение для этого есть: вызвать с помощью Intent какое-нибудь из "посторонних" приложений, использующих ZXing. Эта библиотека предоставляет набор классов и ресурсов для отображения Activity c preview, самостоятельно распознаёт код и возвращает его в ваш метод onActivityResult.
Но как быть, если мы не хотим раскручивать чужие приложения? И встраивать к себе Activity и килограмм ресурсов из клиентской библиотеки ZXing не хочется... Выход есть. Мы можем самостоятельно реализовать всё что нам нужно, используя только ZXing core библиотеку. Давайте посмотрим, как это сделать.

среда, 18 июля 2012 г.

Сервлет для генерации QR-кодов

Раньше я описывал, как используя библиотеку ZXing можно сделать Android-приложение для генерации QR-кода. Теперь то же самое давайте сделаем на серверной стороне. Напишем сервлет, который в ответ на get-запрос с текстом получает изображение в котором этот текст будет закодирован. Такой сервис можно использовать чтобы разместить у себя на сайте любой qr-код картинкой с src="url_сервиса/?code=текст".
Особенных открытий в такой реализации нет, разве что преобразование двумерной матрицы в картинку делаем иначе чем в Android, используя java.awt.* классы.

воскресенье, 15 июля 2012 г.

Реализация Drag and Drop в Android

Touch-интерфейсы дают нам удивительную возможность "прикоснуться к приложению", манипулировать с элементами на экране самым естественным для человека способом. И нам, разработчикам, грех не использовать такую возможность. Давайте разберёмся, как максимально просто реализовать Drag&Drop в нашем приложении.
В двух словах уточним, какое поведение элемента интерфейса мы хотим получить. Есть пара белых ImageView и один синий прямоугольник. Та из белых картинок, которой мы коснулись будет следовать за пальцем, пока мы её не отпустим. Если мы отпустим её над синим прямоугольником, прямоугольник станет красным, и картинка останется на месте. Если за пределами - картинка вернётся на исходную позицию.
Основная наша цель - сделать всё максимально просто. Кроме Layout-a, описывающего начальное состояние интерфейса, мы напишем всего один класс в сотню строк кода. Итак приступим.

пятница, 13 июля 2012 г.

Помехоустойчивое кодирование: реализуем алгоритм Хэмминга на java

В давние суровые времена, окутанные романтикой и легендами, программисты решали совсем другие задачи. Чего стоит, например, передача данных. Мы, скромные наследники гениев, работаем как правило не ниже уровня сокетов. А им приходилось измерять уровни сигнала, а ошибки измерений корректировать. Остались нам от них в наследство десяток алгоритмов коррекции ошибок разной сложности и мощности. Алгоритм Хэмминга достаточно "компромиссный" вариант. При относительной простоте он весьма эффективен: позволяет скорректировать ошибки в один бит на блок и обнаружить ошибки в два бита. Зачем нам может понадобиться его использовать? Например мы решили организовать передачу данных между двумя устройствами "нетрадиционным" способом. Сходу можно придумать три способа передачи данных между двумя Android-смартфонами: с помощью экрана и камеры, динамика и микрофона, вибрации и акселерометра. Как это применить - дело вашей фантазии, а вот как решить проблему коррекции ошибок при этом - подскажет моё маленькое приложение. Давайте рассмотрим реализацию алгоритма подробнее.

четверг, 21 июня 2012 г.

Генерация QR-кода в Android-приложении

Недавно познакомился поближе с замечательной технологией: QR-кодами. Простая вроде бы вещь, а какой простор для фантазии открывается! Тут тебе и обмен ссылками между Android-устройствами, передача контактов, своей "визитки", авторизация... Впрочем, оставляю вам придумать свою идею самостоятельно, а пока вы думаете, покажу как работать с QR-кодами программно. Напишем свой простенький генератор QR-кодов. Сделать это совсем несложно: приложение, скриншот которого вы видите справа, я сделал примерно за 10 минут.
Работу этого приложения вы, кстати, можете проверить тут же: просто наведите на скриншот любой сканер QR-кодов и сравните полученную сторку с тем, что введено в EditText вверху.
Итак, приступим.

понедельник, 18 июня 2012 г.

Добавляем к Android-приложению стили Android 4

Ничто так не портит настроение программиста, как необходимость заниматься вместо программирования чем-то другим. Например, дизайном. Но приложение без дизайна в наше время  обречено на забвение избалованных пользователей. Как же быть? А давайте нашим формам и кнопочкам "выдадим" стандартный стиль Android Ice Cream Sandwich. Пусть наши пользователи, которые пока ещё не обновились до последней версии Android наслаждаются стильным дизайном и проникаются к нашему приложению тёплыми чувствами. В этом добром деле нам поможет библиотека HoloEverywhere. Использовать всю библиотеку мы не будем, нам хватит и одной темы, например моей любимой HoloEverywhereDark. А прикрутим к проекту мы её ручками, попутно изучив механизм "стилизации" Android-приложений.

вторник, 12 июня 2012 г.

Новая версия AndEngine GLES2: продолжаем делать игры для Android


Раньше я писал о том, как легко и быстро можно сделать игру для Android c помощью замечательного 2D игрового движка AndEngine и его "физического" расширения box2D. С тех пор многое изменилось в нашем непостоянном мире. AndEngine теперь поддерживает OpenGL ES2.0, и в связи с этим существенно изменилось его API. Можно, конечно продолжать использовать предидущую версию, но если учесть внушительный список изменений, то такой путь выглядит тупиковым.
Как же быть начинающему разработчику Android-игр, если прочитав несколько руководств и создав своё BaseGameActivity он не увидит там больше описанных везде абстрактных методов onLoadEngine, onLoadResources, onLoadScene, onLoadComplete? Ответ простой: читать этот пост и погружаться в разработку игр с использованием новой версии AndEngine GLES2 :)

четверг, 24 мая 2012 г.

Использование websoсket в Android

Самые интересные Android-приложения, по-моему, те, которые позволяют взаимодействовать пользователям на различных устройствах. Например, сетевые игры, коммуникационные и социальные приложения. В разработке таких программ мы должны использовать широкий спектр технологий, и, соответственно, имеем большой простор для творчества, для построения интересной архитектуры.
Я уже описывал некоторые своеобразные решения для клиент-серверного взаимодействия в мобильных приложениях, но сегодня мы познакомимся, вроятно, с самым интересным инструментом для решения таких задач: с технологией websocket.
В чём её прелесть? Практически полный реалтайм, минимальные накладные расходы на передачу данных, возможность реализовать любой, даже бинарный протокол "внутри" websocket-а, и самое приятное: простота реализации и готовые библиотеки. С этим давайте и разберёмся подробнее.

вторник, 22 мая 2012 г.

Играем с акселерометром в Android

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

понедельник, 14 мая 2012 г.

BroadcastReceiver: общение процессов в Android-приложении

Давайте снова вернёмся к вопросу организации работы фоновых процессов в Android-приложении. В этой статье я описал, как выполнять загрузку файлов с удалённого сервера в фоне. Задача замечательно решается с помощью AsyncTask, но только в случае небольших файлов и недолгого ожидания. Что произойдёт в случае если файл будет загружаться двадцать минут? Телефон уснёт, вы воспользуетесь другим приложением и т.п. - в любом случае загрузка будет прервана. Решение тут очевидно: заменить AsyncTask сервисом, который может работать и без основонго приложения. В случае же использования WakefulIntentService мы практически гарантировано докачаем наш файл несмотря ни на что. И только одну проблему остаётся решить для получения окончательной победы разума над материей: обновлять прелоадер нам нужно в Activity, которая недоступна из сервиса. Выход: использовать внутренний механизм платформы для обмена сообщениями между отдельными компонентами нашего приложения.
Итак, давайте переработаем наше приложения для загрузки файлов так, чтобы оно могло докачать файл при любых обстоятельствах. Научимся использовать BroadcastReceiver.

воскресенье, 13 мая 2012 г.

Работа с GPS и Google Maps в Android

Продолжаем осваивать аппаратные возможности Android-смартфонов. В предыдущих постах мы  изучили, как использовать в наших приложениях микрофон и камеру. Теперь возьмёмся за более сложную тему: GPS. Сложность, конечно же, относительная. В сети есть масса примеров кода, с помощью которого можно получить координаты клиента, поэтому чтобы сделать наш пример интереснее, соединим получение координат с их использованием. В этой статье вы узнаете как получить доступ к Google Maps API, отобразить карту с различными её "плюшками" и вывести на неё наши координаты, полученные различными способами: по спутникам, данным мобильной сети или wifi.
Итак, приступим:

четверг, 10 мая 2012 г.

Работа с камерой в Android-приложении


За что особенно люблю Andriod - так это за обилие аппаратных "плюшек". Тут тебе и микрофон/динамик и gps и wifi, тут и фото/видео камера и так далее... В результате способов применений у этого маленького девайса, а значит простора для программиста, который под него пишет - очень много.
Я уже писал о работе с микрофоном в Android-приложениях, сегодня удостоим внимания камеру. Примеров того, как реализовать простое приложения для фотографирования достаточно много, поэтому в этой статье я покажу кроме всего прочего, как работать с полученной фотографией, в частности как её обрезать. Наше простое приложение может не только сфотографировать вашего знакомого, но и выделить его лицо из кадра, например для сохранения в списке контактов.

понедельник, 7 мая 2012 г.

Пишем скрипты в Google Spreadsheet

В далёких 90-х я начинал осваивать программирование, и первые мои "коммерческие" приложения были написаны на VBA под MS Excel. До сих пор помню как удобно было работать, имея готовый интерфейс, функции для манипуляции данными и адресуемые ячейки... Этот, в принципе, нормальный путь для обучения программированию, сейчас имеет несколько недостатков. Во-первых, нехорошо с первых приложений связывать себя с поприетарной средой разработки и исполнения кода, а во-вторых, кому сейчас нужен Visual basic?. Вот JavaScript и GoogleDoc - другое дело. Да и возможностей, учитывая "web-интерфейсность" среды исполнения и открытые API, существенно больше.
Чтобы помочь тем кто заинтересуется программированием "под ячейки", предлагаю тут простой "урок" по созданию скриптов для Google Spreadsheet.

суббота, 5 мая 2012 г.

Использование OAuth2 авторизации в Android-приложении

Первое, что делает клиент, получив в руки новый Android-девайс - привязывает его к своему google-аккаунту. Эта привязка даёт ему возможность пользоваться всеми сервисами google, а нам, разработчикам - предоставлять ему все эти сервисы с помощью наших приложений. Google даёт нам доступ через API к удивительно богатой инфраструктуре: документы, почта, задачи, поиск, переводчик, распознавание голоса и т.п. И всё это почти бесплатно и очень удобно для клиента с Android в руках. Ключ ко всему этому богатству - OAuth-авторизация. Давайте разберёмся, как ей пользоваться. Наш небольшой пример запросит у клиента доступ к сохранённому на Android-устройстве аккаунту, получит токен, сохранит его для будущего использования, а затем будет полчать список документов в корне его GoogleDoc (или GoogleDrive, как это теперь называется) и отображать полученный список клиенту.

вторник, 24 апреля 2012 г.

Оповещаем Android о событиях на сервере

Допустим, у нас есть достаточно популярное Android-приложение, в которое мы хотим добавить опцию оповещения пользователей о каких-либо событиях. Например, пользователь должен иметь возможность оперативно получить новость или персональное сообщение от другого пользователя. Как решить такую задачу?
Решение в виде сервиса на устройстве, который регулярно опрашивает сервер выглядит несовременно, да и опасно: достаточно большое количество пользователей могут задосить нам сервер. Увеличение интервала опроса в нашем случае решает проблему нагрузки с одной стороны, но снижает скорость доставки с другой.
Второй вариант: использовать Cloud to Device Messaging Framework. Действительно хорошее решение, но в ряде случаев не подходит. Во-первых, передаче сообщений через сторонние сервера могут препятствовать соображения конфиденциальности, а во-вторых, если приложение должно поддерживать версию Android 2.1 и ниже, то этот фреймворк нам не поможет.
Третий вариант - использовать свой http push сервер, например nginx + nginx push stream module. Можно поспорить, что лучше, много запросов или много коннектов а также сравнить производительность разных серверов, но цель у нас сейчас другая. Давайте рассмотрим простой вариант реализации такого оповещения.


среда, 14 марта 2012 г.

Android: отображение форматированного текста в EditText

Однажды мне понадобилось реализовать выделение цветом текста в EditText. На первый взгляд задача казалась нетривиальной, ведь стандартные механизмы форматирования текста для этого виджета весьма убогие. Методами setTypeface, setTextSize и setTextColor можно менять начертание, размер и цвет всего текста, но не отдельных слов. А ведь как было бы здорово использовать обычную html-разметку и "раскрашивать" отдельные участки текста независимо друг от друга...
Собственно, так мы и сделаем :)

Как загрузить html в EditText?
В этом нам помогут два класса: android.text.Html и android.text.Spanned:

public class SpannedTestActivity extends Activity {
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EditText hello = new EditText(this);
        Spanned s = Html.fromHtml("this is <font color=\"red\">red</font>");
        hello.setText(s);
 
        setContentView(hello);
    }
}

Какие теги можно использовать?
Список этих тегов отсутствует в официальной документации, но если порыться в сети или в исходниках Android, то можно накопать следующее:

a href="...", b, big, blockquote, br, cite, dfn, div align="...", em, font size="..." color="..." face="...", h1-h6, i, img src="...", p, small, strike, strong, sub, sup, tt, u

Как видите, в EditText (как и в его предка TextView, кстати) вполне можно вывести форматированный текст.

четверг, 23 февраля 2012 г.

Использование Google Analytics в Android приложении

После того как вы создали и опубликовали своё приложение для Android, наступает приятный и интересный этап: вы наблюдаете, как к вам стекаются первые пользователи. Этот этап редко обходится без открытий и разочарований: клиенты ведут себя совсем не так как ожидалось.
Как же они ведут себя в приложении? Ответ на этот вопрос позволит скорректировать интерфейс и стратегию разработки, иначе подойти к продвижению продукта, если это необходимо. 
Инструментов для сбора статистики в мобильных приложениях достаточно много, но нам после разработки под web привычнее Google Analytics, да и интеграция в приложение у него, как мы увидим сейчас, предельно проста.

четверг, 19 января 2012 г.

Android: загрузка файла из сети с отображением прогресса

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

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.TextView;
 
public class BackFLoaderActivity extends Activity {
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Button load = new Button(this);
  load.setText("Load file");
  load.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    downloadFile("http://anjedi.com/api_lib/2.2_level8.jar");
   }
  });
  setContentView(load, new LayoutParams(LayoutParams.WRAP_CONTENT,
    LayoutParams.WRAP_CONTENT));
 }
 

 private void downloadFile(String url) {
  final ProgressDialog progressDialog = new ProgressDialog(this);
 
  new AsyncTask<String, Integer, File>() {
   private Exception m_error = null;
 
   @Override
   protected void onPreExecute() {
    progressDialog.setMessage("Downloading ...");
    progressDialog.setCancelable(false);
    progressDialog.setMax(100);
    progressDialog
      .setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
 
    progressDialog.show();
   }
 
   @Override
   protected File doInBackground(String... params) {
    URL url;
    HttpURLConnection urlConnection;
    InputStream inputStream;
    int totalSize;
    int downloadedSize;
    byte[] buffer;
    int bufferLength;
 
    File file = null;
    FileOutputStream fos = null;
 
    try {
     url = new URL(params[0]);
     urlConnection = (HttpURLConnection) url.openConnection();
 
     urlConnection.setRequestMethod("GET");
     urlConnection.setDoOutput(true);
     urlConnection.connect();
 
     file = File.createTempFile("Mustachify", "download");
     fos = new FileOutputStream(file);
     inputStream = urlConnection.getInputStream();
 
     totalSize = urlConnection.getContentLength();
     downloadedSize = 0;
 
     buffer = new byte[1024];
     bufferLength = 0;
 
     // читаем со входа и пишем в выход, 
     // с каждой итерацией публикуем прогресс
     while ((bufferLength = inputStream.read(buffer)) > 0) {
      fos.write(buffer, 0, bufferLength);
      downloadedSize += bufferLength;
      publishProgress(downloadedSize, totalSize);
     }
 
     fos.close();
     inputStream.close();
 
     return file;
    } catch (MalformedURLException e) {
     e.printStackTrace();
     m_error = e;
    } catch (IOException e) {
     e.printStackTrace();
     m_error = e;
    }
 
    return null;
   }
 
   // обновляем progressDialog
   protected void onProgressUpdate(Integer... values) {
    progressDialog
      .setProgress((int) ((values[0] / (float) values[1]) * 100));
   };
 
   @Override
   protected void onPostExecute(File file) {
    // отображаем сообщение, если возникла ошибка
    if (m_error != null) {
     m_error.printStackTrace();
     return;
    }
    // закрываем прогресс и удаляем временный файл
    progressDialog.hide();
    file.delete();
   }
  }.execute(url);
 }
}

В этом маленьком приложении при нажатии на кнопку запускается загрузка файла, при этом пользователь наблюдает прогресс-бар. Не забудьте добавить в манифест запрос разрешений на доступ к интернет и файловой системе.
Основой примера является метод, который я взял отсюда (и поправил пару ошибок).  
Этот метод принимает url файла, который нужно загрузить, загружает файл, отображая при этом горизонатальный прогресс-бар. При этом прогресс-бар реально показывает какая часть файла в данный момент загружена. По окончании загрузки файл удаляется.

Разберём работу метода подробнее:

Главная часть метода - создание AcyncTask-а и переопределение его методов.
В методе onPreExecute мы запускаем progressDialog, установив предварительно текст сообщения и максимальное значение прогресса: 100%.
В методе doInBackground - выполняем собственно загрузку файла. Файл читаем  из urlConnection порциями по 1024 байт, каждый раз прибавляя размер полученной порции к общему счётчику. Счётчик и общий размер файла передаём при каждой итерации в метод publishProgress, благодаря чему в методе onProgressUpdate мы получаем эти данные и обновляем текущий статус progressDialog-а.
И, наконец, в методе onPostExecute мы прячем диалог и удаляем временный файл.
Особенностью использования AsyncTask-a является способ, как он объявляется и как в него передаются параметры. Типы, которыми параметризуется экземпляр AsyncTask-a определяю по порядку: тип входящего значения, тип параметра, опреляющего прогресс опреации и тип результата фоновой операции (то что возвращает doInBackground и принимает onPostExecute). Кроме того конструктор и чаcть методов AsyncTask-а принимает varargs, т.е. произвольное число параметров, что весьма удобно в некоторых случаях.

понедельник, 16 января 2012 г.

Android: делаем редактор исходного кода с подсветкой синтаксиса

Иногда, когда под рукой только мой верный Android, хочется чего-нибудь почитать. Например, исходники любимого проекта :) Есть минимум десяток приложений, которые отображают файлы исходников с удобной подсветкой синтаксиса, некоторые из них ещё и позволяют редактировать файлы. Вот мне и стало и нтересно, насколько сложно реализуется такой функционал. Оказалось - очень просто.
 
Итак, для приготовления "блокнота с подсветкой синтаксиса" в Android нам понадобится:
  1. Хорошая библиотека для подсветки синтаксиса на JavaScript+CSS. Можно выбрать тут, мне больше всего понравилась CodeMirror, её и будем использовать. 
  2. WebView для интеграции всего этого богатства с нашим приложением
  3. Немного кода, для взаимодействия с WebView и работающим в нём JavaScript.