PhoneGap — решение проблем в первом приложении

    PhoneGap — HTML5 платформа для разработки приложений под 7 существующих мобильных платформ. На днях они обновились до версии 1.2.0 и переехали под крыло Апачи. Адрес репозитория также обновился. На сайте в документации замечательная пошаговая инструкция для создания первого приложения, так что на вводной части больше не будем останавливаться, об этом уже писали. Мелкие проблемы начинаются, когда немножко выходишь за рамки стандартных возможностей платформы, но все решаемо.

    Постановка задачи


    Захотел я сделать нидерландско-русский словарь под Андроид за неимением приличного на рынке. Мне попался словарь отсканированный с хорошего бумажного. Процесс извлечения информации с картинок заслуживает отдельной статьи. Поэтому пропустим эту часть. В качестве первого этапа сделал словарь в виде html странички. По инструкции по созданию «hello world» сделал приложение и оно успешно запустилось. Дальше надо было обработать напильником и довести до ума детали.

    Настройки

    Для хранения настроек воспользовался window.localStorage. Сделал на основной же html страничке диалог с нужными полями ввода и повесил обработчик на события 'menubutton', 'backbutton', чтобы его показывать/прятать/сохранять. Все работает как надо. Позже добавил туда же хранение истории запросов.

    Надпись на дефолтовой кнопке

    При открывании софтверной клавиатуры дефолтовая кнопка имеет надпись 'GO'. Что не сильно соответствует работе со словарем. Документация фреймворка такую ситуацию никак не освещает. Документация в SDK говорит про установку атрибута android:imeOptions поля ввода. Но конечно внутри браузера это не работает. Несколько раз перерыл инет в поисках решения. Наконец наткнулся на рабочий вариант под iPhone — вместо <input type="text" /> надо использовать <form><input type="search" /></form> Причем элемент формы обязателен. Под Андроид работает и без формы, а форма даже вредна, так как происходит ее отправка и надо это дело предотвращать. Итак используем <input type="search" /> и дефолтовая кнопка волшебных образом превращается в 'Search'.

    Выход из приложения

    Во фреймворке есть API для этого — navigator.app.exitApp(). Как оказалось, просто из коробки это не работает. Чтобы заставить это работать в AndroidManifest.xml надо прописать <uses-sdk android:minSdkVersion="2" />. В новой версии 1.2.0 проблему уже исправили, так что это еще один повод обновиться.

    Перевод из буфера обмена

    В читалке(CoolReader) есть копирование слова в буфер. Поэтому, чтобы не делать лишних телодвижений при вызове словаря, а получать сразу перевод, хотелось получить доступ к буферу обмена. По умолчанию такой возможности нет, но платформа легко расширяется плагинами. Нашел плагин, который дает API для работы с буфером. В комплекте оказалась еще куча других (27 штук) — phonegap-plugins. Загляните, не пожалеете.
    Теперь процесс выглядел так — увидел незнакомое слово, два раза тапнул( слово в буфере), вызвал менеджер задач( длинный тап), выбрал словарь(еще один тап) и опа — перевод уже тут. Нажал отмену — и снова в читалке.

    Автоматический вызов и перевод

    Но нет предела совершенству. Хотелось еще меньше телодвижений. Читалка имеет интеграцию с 2мя словарями — Fora Dictionary и 2 способа вызова ColorDict. Скачал исходники читалки, благо они открыты, посмотрел как происходит вызов. К сожалению оба словаря зашиты, хотя и вызываются стандартным способом и могли бы конфигурироваться. Поэтому возникла мысль прикинутся, например, форой. Этой инструкции из документации SDK вполне достаточно, чтобы реализовать нужный поисковый интерфейс, получать переданную для поиска строку и передавать ее параметром в урле. Стандартная реализация onCreate стала выглядеть так:
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    
    		Intent intent = getIntent();
    		String url = "file:///android_asset/www/index.html";
    		if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    			url += "?search=" + intent.getStringExtra(SearchManager.QUERY);
    		}    	
    		super.loadUrl(url);
    	}
    

    Но этот код передает строку поиска только при создании приложения. Чтобы получать строку поиска, когда приложение уже находится в фоне, понадобилось добавить еще один метод в стандартный ява-код:
    	@Override
    	protected void onResume() {
    		super.onResume();
    		Intent intent = getIntent();
    		if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
    			String sQuery = intent.getStringExtra(SearchManager.QUERY);
    			this.appView.loadUrl(
    				"javascript:try{PhoneGap.fireDocumentEvent('search', {detail: '" 
    				+ sQuery + "'});}catch(e){};"
    			);
    		}    	
    	}
    

    Код генерит новое событие 'search' для яваскрипта и передает нужные данные. Как создавать свои события подсмотрел в исходных кодах PhoneGap.
    Чтобы читалка думала, что вызывает фору, пришлось переименовать классы. Нужное имя подсмотрел в исходниках читалки. Это конечно не совсем правильно, но для частного употребления сгодится.
    Теперь использование словаря стало идеальным — увидел незнакомое слово, два раза тапнул — словарь показал перевод, нажал отмену — снова в читалке.

    Выпадение приложения при смене ориентации экрана

    После добавления поискового интерфейса и переименования классов в новый проект приложение стало вываливаться при смене ориентации с ошибкой «couldn't save which view has focus because the focused view… has no id». Так как поиск показал, что это проблема случается, то упомяну как это порешалось. Что именно оказалось решающим я не стал разбираться. При создании нового проекта я пропустил в AndroidManifest.xml
    — в корневом элементе manifest:
    	<supports-screens
    		android:largeScreens="true"
    		android:normalScreens="true"
    		android:smallScreens="true"
    		android:resizeable="true"
    		android:anyDensity="true"
    	/>
    

    — в application элементе не создал activity:
     			<activity 
    				android:name="com.phonegap.DroidGap" 
    				android:label="@string/app_name" 
    				android:configChanges="orientation|keyboardHidden">
    				<intent-filter></intent-filter>
    			</activity>        
    

    Во всех activity нужен атрибут android:configChanges="orientation|keyboardHidden". Конечно это я сам накосячил, т.к. в «hello world» все это есть. Просто будьте внимательны.

    Приложение получилось очень скромных размеров — 186к (не считая словарных данных). Хотя первоначально была идея после PhoneGap сделать все по стандартному процессу, но т.к. результат устраивает на все 100%, то смысла в этом уже не вижу.

    ЗЫ. спасибо Vadim Lopatin за Cool Reader с исходными текстами, спасибо создателям PhoneGap за доступную платформу с исходными текстами, спасибо создателями плагинов с сорцами, гитхабу за доступ к исходникам, и битбакет за хранение моих.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 20

      +2
      Заинтересовался разработкой с помощью PhoneGap. Я правильно понимаю, что приложение пишется на js+html с использование библиотечных функций phoneGap, которые предоставляют доступ к нативным функциям платформ? Т.е. после написания нужно лишь откомпилировать приложение под определенную платформу, и оно будет работать? Или нужно сделать что-то ещё?
        +2
        Да, все верно, все очень просто, пока не полезут такие проблемы как у меня. Тогда гугл, гугл и еще раз гугл :) и конечно документация по SDK и платформе.
        0
        Может я чего-то еще недопонял, но от Phonegap смешанные чувства. С одной стороны, js/html позволяют ускорить разработку, а открытость исходников еще и греет душу, но: 1) как же нативный интерфейс? хотя бы меню… 2) как ускорить компиляцию — уж больно долго phonegap.jar конвертируется в classes.dx, нативное приложение компилится на глаз быстрее 3) с жизненным циклом activity все в порядке будет если использовать phonegap? когда сохранять данные?
        Да, видимо разбираться и разбираться мне еще…
          0
          1. можно реализовать нативные вещи в виде плагина на яве с апи в яваскрипт. Посмотри исходники плагинов, там все достаточно просто.
          2. работаю на 5 летнем ноутбуке, особых тормозов не заметил. Проблема была с отладкой в эмуляторе. Приложение сделанное под старый апи там не работало. Поэтому отлаживался на телефоне. Последнюю версию еще не смотрел. Может она работает в эмуляторе.
          3. о разных сменах состояния приложение уведомляется ивентами, так что проблем не должно быть. Список событий можно расширить, как это проделал я.
            +3
            Забудте про активити. Phonegap — скомпиленая нейтив заглушка, которая в себе просто открывает встроенный браузер смартфона, с html+css+js файлами. Вот и весь Phonegap.
              0
              Никакой встроенный браузер не открывается, по крайней мере на iPhone и Android (с другими дел не имел). Если говорить про Android, то все PhoneGap приложение крутиться внутри одной активности с единственным виджетом — WebView. Последний предоставляет рендеринг html и исполнение JS.

              Если говорить про написание нативных плагинов — на это WebView можно накидать много чего дочернего, т.к. оно наследует AbsoluteLayout (осторожно, depricated!).
                0
                WebView — это компонент со встроенным браузером смартфона.
              +1
              1) Нативный интерфейс не может быть кроссплатформенным (того же меню на iOS к примеру нет).
              2) PhoneGap.jar — это набор классов для организации моста JavaScript — Java с доступом к телефону, чем больше классов, тем дольше компиляция, это нормально. Любое другое приложение с большим кол-во классов или библиотек будет долго компилироваться.
              3) PhoneGap — это одно Activity c WebView внутри (за исключением работы некоторый плагинов), а данные сохраняются в LocalStorage.

              PhoneGap — это оболочка для запуска html5-application на различных платформах. В идеале написанное html5-application должно работать точно так же и при простом заходе через браузер, не даром в документации можно увидить, что методы точно такие же, как и в обычном js (взять к примеру работу с LocalStorage или sqlite)
                0
                1) Да, хотя это несколько сужает круг использования PhoneGap. Почему-то в библиотеки имитирующие нативный интерфейс мне не верится. А делать меню/настройки/диалоги через плагины — теряется вкус легкой жизни с Phonegap.
                2) Я понимаю, что добавление новых классов замедляет сборку, но от сборки к сборке я изменяю только «assets/www», ни один *.java не изменяю — кажется достаточно только перепаковать архив .apk? Ведь classes.dx остается неизменным.
                3) Да, после работы с Андроидом чё-то даже не подумал что данные можно сохранить тогда когда это нужно приложению, а не системе :)
              +1
              Не кроссплатформенно у вас кое-где получилось, наверное вам это не сильно нужно, но раз уж выбрали средства для кроссплатформенной разработки, то мне кажется их нужно придерживаться.

              localstorage в ie9 с локального домена не будет работать. Там для хранение сетингов наверное должно быть что-то поверх isolated storage(изолирование хранилище или БД), которое предоставляет ос для приложения.
                0
                Так как это приложение чисто для персонального использования, то кроссплатформенностью я не замарачивался. Приложение нельзя выставить для раздачи, т.к. оно использует данные «с душком».
                Для хранения можно воспользоваться апи, который предоставляет платформа — storage.
                Но, как говорится, «вам шашечки или ехать?» мне — ехать, поэтому я воспользовался более удобным интерфейсом.
                  0
                  ну я так просто на всякий пожарный вдруг полезно окажется.
              • НЛО прилетело и опубликовало эту надпись здесь
                  –1
                  Адоби его не покупали.
                  • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Если в приложение надо встроить сканер штрих-кодов, то ка поставляются плагины?
                  1) Для каждой из платформ свой плагин. Тогда как будет работать приложение запущенное на WP7 и BlackBerry, если для этих платформ плагинов нет?
                  2) Или же один «универсальный» палагин сразу на все платформы? Как компилируется исходник плагина?
                    0
                    Вы ж под каждую платформу будете делать отдельное приложение. Каждая платформа имеет свой цикл разработки и поддерживаются разные языки. iPhone — objective C, Android — java, WP7 — silverlight и т.д. Поэтому да — разные платформы, разные плагины. Но плагины могут обеспечивать одинаковый апи для яваскрипта. Так что в приложении будет универсальный код.
                    Если плагина нет, то надо будет писать самому или искать код, который переделать в плагин.
                    0
                    Погодите, а как html+js приложение взаимодействует с базой данных словаря? Из js можно к sqlite обращаться? Или у вас он в офлайне не работает?
                      0
                      Ответ ниже
                      0
                      Да, у phonegap есть интерфейс для работы с базой данных. Но это не мой случай. У меня нет базы слов. Если бы была, то и огород не стоило городить. У меня словарь состоит из сканированных со словаря страниц. Он показывает картинки. Картинки могут лежать в папке на телефоне или в онлайне на сервере.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое