Пишем клиент для любимого сайта на phoneGap

  • Tutorial
Бывает что есть сайт, но пользоваться им с мобильного телефона не очень удобно и было бы замечательно иметь отдельное приложение для него. Любители веб разработки легко смогут это сделать использую phoneGap. Под катом речь пойдет про сервис отслеживания почтовых отправлений, которым я часто пользуюсь и написанный для него демонстрационный клиент с базовым функционалом: авторизация, получение списка треков и описания к ним и возможность добавить новый трек. Я не связывался с разработчиком по этому поводу и очень надеюсь что он на меня не обидится за наглость, за копание в JavaScript коде и за то что в приложении не будет показана реклама. Так же прошу простить за костыльность и велосипедность кода, воспринимайте его как пример.

Статья подразумевает, что вы сами сможете разобраться как работает сайт, информация с которого будет отображаться в приложении.
Те кто читали прошлую статью об авторизации через некоторые сайты уже знаю что в phoneGap есть замечательная штука inAppBrowser, которая не только открывает сайт внутри приложения, но и позволяет внедрить в него свой JavaScript код и поменять стили, что пригодится для форм авторизации.
На её основе, по образу и подобию авторизации на хабре был написан вот такой код:
var plugin_www_post_tracker_ru = {
		wwwref: false,
		authOk: false,

		auth: function (force) {
			if (!window.localStorage.getItem("plugin_www_post_tracker_ru_PHPSESSID") || force) { //если нет данных об авторизации или принудительная авторизация
				var authURL="http://post-tracker.ru/login.php";
				this.wwwref = window.open(encodeURI(authURL), '_blank', 'location=no'); // открываем сайт внутри приложения
				this.wwwref.addEventListener('loadstop', this.auth_jsinjection); // по завершении загрузки делаем инъекцию в код
			} else {
				plugin_www_post_tracker_ru.authOk=true;
			}
		},
		
		auth_event_url: function (url) {
			var tmp=url_parser.get_args_cookie(url); // парсин ответов с куками
			if (tmp['PHPSESSID'] && tmp['userid'] && tmp['securehash']) { // если все нужные куки есть, то видимо авторизировалис
				plugin_www_post_tracker_ru.wwwref.close(); // закроем браузер и сохраним полученные параметры
				window.localStorage.setItem("plugin_www_post_tracker_ru_PHPSESSID", tmp['PHPSESSID']);
				window.localStorage.setItem("plugin_www_post_tracker_ru_userid", tmp['userid']);
				window.localStorage.setItem("plugin_www_post_tracker_ru_securehash", tmp['securehash']);
				plugin_www_post_tracker_ru.authOk=true;
				перейдем тут на получение контента
			}
		},
		
		auth_cssinjection: function(){
			plugin_www_post_tracker_ru.wwwref.insertCSS({code:".topline {display:none} .top {display:none} .logo {display:none} .menu {display:none} .counters {display:none} .bottom {display:none} .links {display:none} @-viewport {width: device-width; zoom: 1;"},function(){});
		},
		
		auth_jsinjection: function () {
			plugin_www_post_tracker_ru.auth_cssinjection(); // вставляем CSS код в форму авторизации, для скрытия лишних блоков
			plugin_www_post_tracker_ru.wwwref.executeScript({ // JS код для получения куков
	            code: "document.cookie;"
	        }, function(arg) {
	        	plugin_www_post_tracker_ru.auth_event_url(arg);
	        });
		}
}


Для получения контента будем использовать небольшую AJAX библиотеку, способную делать POST и GET запросы, которая умеет вставлять cookie и отдающая полученный результат в callback функцию (основа взята из miniajax):
AJAX
var ajax = {
	init: function(){
		return new XMLHttpRequest();
		},
	send: function(url,method,args,cookies,async,_callback){
		var q=ajax.init();
		q.open(method,url,async);
		q.onreadystatechange=function(){
				if(this.readyState==4 && this.status==200) {
					_callback(this.responseText);
				}
			};
		if (cookies) {
			q.setRequestHeader('Cookie',cookies);
		}
		if(method=='POST') {
			q.setRequestHeader('Content-type','application/x-www-form-urlencoded');
			q.send(args);
		} else {
			q.send(null);
		}
	}
}

Для получения контента делаем GET запрос на страничку post-tracker.ru/my/ при этом, если в ответе в одном из блоков мы встретим строку Войдите или Зарегистрируйтесь, то нас явно разлогинили и придется пройти процедуру еще раз:
		get_content: function (async) { // делам запрос на страницу с трек кодами
			var cookies="PHPSESSID="+window.localStorage.getItem("plugin_www_post_tracker_ru_PHPSESSID")+"; userid="+window.localStorage.getItem("plugin_www_post_tracker_ru_userid")+"; securehash="+window.localStorage.getItem("plugin_www_post_tracker_ru_securehash"); // формируем cookie
			plugin_www_post_tracker_ru.dataReady=false;
			ajax.send("http://post-tracker.ru/my/",'GET',null,cookies,async,this._parse_content); // делаем ajax запрос и посылаем его содержимое для распарсивания
		},
		
		_parse_content: function (data) {
			var wrapper=document.createElement('div'); // заворачиваем html код внутрь объекта, чтобы удобнее с ним работать
				wrapper.innerHTML=data;
			plugin_www_post_tracker_ru._get_inside_data(wrapper,'trackcode,date,status,comment'); // и отправляем его на получение данных о треккоде, даты последнего изменения, статусе и описании посылки
		},
		
		_get_inside_data: function (wrapper,types) {
			var tmp=wrapper.getElementsByClassName('login')[0].innerHTML; // на всякий случай получаем содежимое div блока login
			if (tmp=='<a href="/login.php">Войдите</a> или <a href="/register.php">Зарегистрируйтесь</a>') { // если там такое, то придется авторизироватья
				plugin_www_post_tracker_ru.auth(true);
			} else { // а иначе продолжаем
				plugin_www_post_tracker_ru.default_folder=wrapper.getElementsByTagName('input')[0].value; // получи ID стандартной папки с кодами, чтобы в дальнейшем можно было добавлять туда новые коды отправлений
				var types=types.split(",");
				for (var typeid in types) { // разбираем html-ку по блокам с заданными типами
					var tmp=wrapper.getElementsByClassName(types[typeid]);
					var id=0;
					for (var i in tmp) {
						if (tmp[i].innerHTML) {
							if (!plugin_www_post_tracker_ru.postdata[id]) plugin_www_post_tracker_ru.postdata[id]=new Array();
							plugin_www_post_tracker_ru.postdata[id][(types[typeid])]=tmp[i].innerHTML.replace(/(\r\n|\n|\r)/gm,"").replace(/<\/?[^>]+>/gmi,"").replace(/^\s+|\s+$/gm,""); // убираем все ненужности в виде переносов строки, html кода для оформления и лишние пробелы
							id++;
						}
					}
				}
				plugin_www_post_tracker_ru.dataReady=true;
				show_list(); // даем команду на отображение полученных данных
			}
		},
		
		get_list: function () { // это будет запрошено функцией show_list() для "красоты" оформления
			var tmp=new Array();
			for (var i in plugin_www_post_tracker_ru.postdata) {
				tmp[i]="<b>"+plugin_www_post_tracker_ru.postdata[i]['trackcode']+"</b> "+plugin_www_post_tracker_ru.postdata[i]['comment']+"<br/>"+plugin_www_post_tracker_ru.postdata[i]['date']+" "+plugin_www_post_tracker_ru.postdata[i]['status'];
			}
			return tmp;
		}


show_list() в данном коде добавит блоки с подготовленной html-кой внутри div-а со скроллером (кстати рекомендую Overthrow и крайне не рекомендую iscroll-4 по причине ужасной скорости работы).

Настала пора добавить возможность отправки новых кодов для отслеживания, но сперва добавим действие на кнопку MENU вашего Android смартфона. Делается это с помощью события menubutton. К которому мы привяжем появления блока с кнопкой «Добавить трек код», которая, при нажатии, начнет процесс.
		put_trackCode: function(){ // то что будет выполнятя после нажатия на кнопку "Добавить трек код"
			var trackCode = prompt("Введите трек код"); // запросим у пользователя сам треккод
			if (trackCode) {
				this._put_trackCode_getPath(trackCode); // запросим у сайта направление для этого кода (например из Китая в Россию), внимание тут снова не используем асинхронный запрос
				if (plugin_www_post_tracker_ru.default_path) { // если получили направление, добавим описание
					var comment = prompt("комментарий");
					if (comment) {
						this._put_trackCode(trackCode,comment); // и отправим эти данные на сайт
					}
				} else {
					alert("Не правильный трек код");
				}
			} else {
				menuButtonHide(); // если пользователь не захотел вводить, то не забудем скрыть блок с менюшкой
			}
		},
		_put_trackCode_getPath: function (trackCode) { // запрашиваем направление
			var cookies="PHPSESSID="+window.localStorage.getItem("plugin_www_post_tracker_ru_PHPSESSID")+"; userid="+window.localStorage.getItem("plugin_www_post_tracker_ru_userid")+"; securehash="+window.localStorage.getItem("plugin_www_post_tracker_ru_securehash");
			var data="act=getPathForm&trackcode="+encodeURIComponent(trackCode);
			ajax.send("http://post-tracker.ru/ajax/userTrackcodes.php",'POST',data,cookies,false,this._put_trackCode_getPath_result);			
		},
		_put_trackCode_getPath_result: function (data){
			var wrapper=document.createElement('div'); // заворачиваем ответ, представляющий собой html
			wrapper.innerHTML=data;
			var tmp=wrapper.getElementsByTagName('input'); // смотрим есть ли в ответе input
			if (tmp.length>0) {
				plugin_www_post_tracker_ru.default_path=tmp[0].value; // если есть, то значение в нем нам очень пригодится
			}else{
				plugin_www_post_tracker_ru.default_path=false;
			}
		},
		_put_trackCode: function (trackCode,comment){ // отправляем треко код и комментарий к нему, снова не асинхронно
			var cookies="PHPSESSID="+window.localStorage.getItem("plugin_www_post_tracker_ru_PHPSESSID")+"; userid="+window.localStorage.getItem("plugin_www_post_tracker_ru_userid")+"; securehash="+window.localStorage.getItem("plugin_www_post_tracker_ru_securehash");
			var data="act=addTrackcodeAction&folderid="+plugin_www_post_tracker_ru.default_folder+"&trackcode="+encodeURIComponent(trackCode)+"&path="+plugin_www_post_tracker_ru.default_path+"&comment="+encodeURIComponent(comment);
			ajax.send("http://post-tracker.ru/ajax/userTrackcodes.php",'POST',data,cookies,false,this._put_trackCode_result);
		},
		_put_trackCode_result: function(data){ // данные отправились
			menuButtonHide(); // скрываем меню
			plugin_www_post_tracker_ru.get_content(true); // и снова запрашиваем содержимое страницы с кодами посылок
		}


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

Исходный код проекта и собранное приложение для тестов и экспериментов заинтересованных лиц. А так же гитхаб для менее унылых форков github.com/SovGVD/PhoneGap-post-tracker.ru

Only registered users can participate in poll. Log in, please.

Продолжать ли писать о phoneGap и эксперименты с ним?

  • 82.6%Да, вполне интересно на что сгодится html/css/js для мобильных приложений667
  • 17.4%Нет, писать что-то на веб технологиях для телефонов — безумие140

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 18

    +1
    Спасибо за статью! Хотелось бы узнать о плюсах/минусах такого подхода к созданию приложений. Для себя пришел к следующему выводу, бегло просмотрев подобные решения, что это довольно быстрый способ создания приложения, и если нет ресурсов для разработки нативных приложений, а оно очень нужно, то это почти идеально. А как Вы пришли к такому выбору? Насколько мне известно, LinkedIn тоже сделали свое прложение с помощью js/html/css, рассматривали их путь как возможный вариант решения Вашей задачи?
      +2
      Я делал приложение под phoneGap. Если вкратце то самая большая проблема это медленная работа и отсутствие библиотек предназначенных для этого. С картами еще можно разобраться, но они работают в браузере ожидаемо хуже чем нативные, но сделать хорошую галерею в iOS стиле точно не получится.
      Когда я с этим возился даже для того чтобы быстро и без задержки обработать нажатие (изза ограничений WebKit) нужно было ставить в код специальные хаки, иначе оно просто тормозило.

      Приложение работает медленнее и выглядит не так в итоге. Как будто это приложение с какого-то китайского клона а не с этой платформы.
      После того как я решил перейти на нативные библиотеки все сразу стало проще и веселее.
        0
        От себя хочу добавить невозможность отлаживать JavaScript код, запущенный под PhoneGap/Cordova. Нельзя поставить breakpoint, посмотреть стек вызовов, узнать значения переменных. Отлаживать вёрстку, CSS и Network можно с горем пополам используя Weinre. И частенько приходилось использовать console.log(...), чтобы хотя бы в логах ADB под Android-ом получить какую-нибудь информацию.
        • UFO just landed and posted this here
        0
        В общем то вы описали плюсы, а человек ниже — минусы.
        Если у вас команда веб разработчиков и без особых усилий нужно сделать приложение для кучи мобильных операционок, то phoneGap вам в руки, но если ваш front-end девелопер не может жить без громоздких библиотек, то скорее всего будет много проблем с производительностью и вы даже плюните на это дело. Мне больше нравится писать чистый код (используя библиотеки для рутинных задач, вроде ajax запросов или распарсивание без eval json объектов), поэтому проблем не возникло. Еще к плюсам можно отнести то, что попутно сможете использовать уже наработанный JS код проекта и написать расширения для браузеров вроде Chrome, Opera и Firefox.
        Пришел к phoneGap я на прошлой неделе всего лишь, когда Nokia написала о своей платфоре Asha, мол там можно веб приложения делать, решил попробовать, оказалось что всё довольно уныло в плане возможностей (привет html4), а приложение какое нибудь для телефонов захотелось написать не имею желания и времени изучать Java, Object C и прочие языки для каждой из платформ.
        По поводу внешнего вида спорный вопрос, т.к. сейчас на своем Android телефоне вижу дикий зоопарк GUI, так почему бы не сделать еще один вариант.
        Нашел apk-ку LinkedIn, не заметил там phoneGap (вероятно свой вариант вебкит обертки или еще какой подобный проект), но версия 2.4 написана именно на html+js с использованием библиотек underscore.js и Backbone.js (не знаком с обоими, поэтому без комментариев).
        Яро призывать использовать это всё не буду, так же как и не буду говорить что phoneGap не годится для разработки приложений. Но мне очень понравилось.
          +2
          Ну вот и итог: phonegap можно посоветовать тем, кто очень не хочет изучать предназначенные для мобильной разработки технологии, но очень хочется сделать здесь и сейчас не самое качественное приложение. Для тех, у кого мобильное приложение это приоритетная вещь, использовать phonegap не нужно — себе дороже выйдет. Я в этом убедился на собственном опыте.
            0
            Да, именно так, если надо как можно быстрее выстрелить без затрат и приложение для телефона не главное, то phoneGap очень поможет.
        +1
        phoneGap это вполне хорошая штука, пока вы не наткнулись на хранение данных :) Работа с браузерным SQL в нем жутко неудобная, и хранить он может очень мало данных. Тот же titanium в этом плане намного удобнее.
          +1
          Попиарили, попиарили давно mobile-разработку на html/js, так и осталось это уделом ярых фротнэндщиков.
            0
            Абсолютно согласен — причем даже ниже попытался обяснить почему. Просто верстка и JS должны быть выше среднего, или… иметь готовый фреймверк
            +4
            Уважаемый, NermaN
            Я не буду доказывать Вам что вы правы или не правы. Просто хотелось бы сделать пару замечаний:

            1)
            (кстати рекомендую Overthrow и крайне не рекомендую iscroll-4 по причине ужасной скорости работы).
            Сравнение абсолютно не корректно: Overthrow — полифил, iscroll-4 — либа. Кроме того о каком nativelook может идти реч если Overthrow, по умолчанию добавляет rabber эффект на iOS и дропает все z-index внутри себя. iScroll — он НАМНОГО более широк в функциональном плане, и у него намного больше возможностей — это не просто полифил скрола. И если у Вас на андроиде проблема с ним — скорее всего вы не правильно наверстали страничку, он просто сильно критичен к корректной верстке

            2)
            но сделать хорошую галерею в iOS стиле точно не получится.
            Можно и делали, причем со свайпом и 3D, сразу и под андроид и под иос. Основные тонкости которые нужно учесть при этом это:
            а) 300ms
            б) галерея = адаптер вью
            в) трансформации должны быть аля -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); причем в покое тоже должна быть трансформаця с нулевыми параметрами что бы избежать первоначатьного дерганья. А на андроиде к свайпу добавил бы -webkit-transform: translateZ(0); и включил hardware acseleration в манифесте

            3)
            Приложение работает медленнее и выглядит не так в итоге. Как будто это приложение с какого-то китайского клона а не с этой платформы.
            Тут даже сказать нечего — просто не согласен и все, релизнули более полутора десятков апликух на фонгапе, замечаний небыло.

            Основная проблема при начале работы с фонгап состорит в следующем в непонимании следующих моментов:
            1)PhoneGap это не web — это абсолютно другая специфическая платформа
            2)PhoneGap — выполняется на различных МОБИЛЬНЫХ устройствах, поэтому он НАМНОГО более требователен к оптимизации и знанию тонкостей работы браузера, и лепить в одно приложение несколько десятков либ(одна скролит, одна работает с DOM, одна биндит данные, одна структурирует код и т. д. ) здесь не очень корректно — представьте вы налепили эскадру из «звездолетов» и хотите чтобы эта эскадра довезла у вас один единственный ящик. А топлива то жрать будет вся эскадра. Это я не про Вас, просто по опыту код ревью разных PhoneGap «шедевров»
            3)В PhoneGap НЕТ UI — это платформа для доступа из html/js приложения к нативной функциональности мобильного устройства такой как компас, БД, акселерометр, контакты и т. д. Представте на секунду что у вас нет нативной реализации лист вю(адаптер вю) — скажите долго ли Вы будете его реализовывать? Вот и я о томже — я пока на данный момент не встречал пока КОРРЕКТНО заточеный, КРОССПЛАТФОРМЕНЫЙ UI JS фреймверк для мобильных устройств? Sencha JQMobile и т. д. — лучше не обсуждать — просто ОЧЕНЬ ДОЛГО = у каждого свои недостатки, а изучили мы их более 3 десятков

            Поэтому на фонгапе можно писать: это всегда будет дольше чем нативное приложение под одну конкретную платформу. Но это всегда будет меньше чем под две платформы, даже с учетом оптимизации UI под каждую платформу. И конечно же как и на любой другой платформе — ВАМ НУЖНО ЗНАТЬ ТОНКОСТИ ПЛАТФОРМЫ(немного само рекламы — костно язычный доклад о тонкостях фонгапа, слайды)
              0
              небольная статейка к докладу сорри на английском, если кому влом смотреть видео
                0
                Вам бы на хабре туториалы пописать о phoneGap, зная все тонкости. Я то эту штуку мучаю по вечерам (и то если не с ребенком сижу) в течении одной недели, тонкостям пока не научен.
                  0
                  Не могли бы вы, пожалуйста, поделиться ссылками или ресурсами, где вы изучали, как говорите «шедевры», то есть любые исходники приложений, как хороших, так и плохих, сложно найти что-то из этой серии, без разницы на каком языке, можно на английском. Сильно заинтересовал PhoneGap, когда из последних кейсов у них на сайте увидел приложение ex.fm, поставил на телефон и удивился насколько эта технология выросла и, в принципе, приложение выглядело вполне нативно. Спасибо.
                    0
                    Использовали ratchet — это весьма примитивный фреймворк, на android проблемы, зато прост и быстр.
                      0
                      так ratchet же специально под айфон сделан, поэтому ничего удивительного)
                    0
                    Спасибо за начинание
                    Вместо большого количества кода я бы предпочел ссылки на github или другие ресурсы, оптимизированные под это.
                      0
                      Честно говоря не уверен что есть смысл выкладывать этот код куда то, т.к. это пример, а не законченное приложение или начинание такового.
                      Смысл кода тут в комментариях к нему, которые почти не заметны. А как оформить более понятно, я не придумал.

                    Only users with full accounts can post comments. Log in, please.