titanium alloy android push通知 by ruby

前準備

Google Developers Consoleから
新しいプロジェクトを作成し、プロジェクトナンバーを控える

Project Number: 333333334444444 # GCM sender ID

APIs & auth > APIsからGoogle Cloud Messaging for AndroidをOFF→ONに
APIs & auth > CredentialsからPublic API access→Create new keyと進みServer keyを作成

AIzaSyCZzdkY35DyGGDzdw7WFz0F1pxxvk_M # API KEY
サーバー(heroku+mongolab)

android端末から得られるdeviceTokenが通知に必要なのでmongodbに保存&通知処理

require "mongoid"
require "sinatra"
require "gcm"
if development?
  require 'sinatra/reloader' 
end

Mongoid.load!("mongoid.yml", :development)

class Registration
  include Mongoid::Document
  field :cd, type: String
end

post "/gcm" do
  Registration.find_or_create_by(cd: params[:id])
end

get "/" do
  erb :index
end

post "/registration" do
  gcm = GCM.new("<API KEY>")
  registration_ids= Registration.all.pluck(:cd)
  options = {data: {title: params["title"],message: params["message"]}}
  @res = gcm.send(registration_ids, options)
  erb :gcm
end
titanium

githubからモジュール取得して配置
iamyellow/gcm.js · GitHub

/modules
  /android
    /net.iamyellow.gcmjs

tiapp.xml

<property name="GCM_sender_id" type="string"><GCM sender ID></property>
<modules>
	<module platform="android" version="0.2">net.iamyellow.gcmjs</module>
</modules>

index.js

var gcm = require('net.iamyellow.gcmjs')

var pendingData = gcm.data;
if (pendingData && pendingData !== null) {
	// if we're here is because user has clicked on the notification
	// and we set extras for the intent 
	// and the app WAS NOT running
	// (don't worry, we'll see more of this later)
	Ti.API.info('******* data (started) ' + JSON.stringify(pendingData));
}

gcm.registerForPushNotifications({
	success: function (ev) {
		// on successful registration
		Ti.API.info('******* success, ' + ev.deviceToken);
	},
	error: function (ev) {
		// when an error occurs
		Ti.API.info('******* error, ' + ev.error);
	},
	callback: function () {
		// when a gcm notification is received WHEN the app IS IN FOREGROUND
		alert('hellow yellow!');
	},
	unregister: function (ev) {
		// on unregister 
		Ti.API.info('******* unregister, ' + ev.deviceToken);
	},
	data: function (data) {
		// if we're here is because user has clicked on the notification
		// and we set extras in the intent 
		// and the app WAS RUNNING (=> RESUMED)
		// (again don't worry, we'll see more of this later)
		Ti.API.info('******* data (resumed) ' + JSON.stringify(data));
	}
});

app/assets/android/gcm.js

/*global Ti: true, require: true */

(function (service) {

	var serviceIntent = service.getIntent(),
	title = serviceIntent.hasExtra('title') ? serviceIntent.getStringExtra('title') : '',
	statusBarMessage = serviceIntent.hasExtra('message') ? serviceIntent.getStringExtra('message') : '',
	message = serviceIntent.hasExtra('message') ? serviceIntent.getStringExtra('message') : '',
	notificationId = (function () {
		// android notifications ids are int32
		// java int32 max value is 2.147.483.647, so we cannot use javascript millis timpestamp
		// let's make a valid timed based id:

		// - we're going to use hhmmssDYLX where (DYL=DaysYearLeft, and X=0-9 rounded millis)
		// - hh always from 00 to 11
		// - DYL * 2 when hour is pm
		// - after all, its max value is 1.159.597.289

		var str = '',
		now = new Date();

		var hours = now.getHours(),
		minutes = now.getMinutes(),
		seconds = now.getSeconds();
		str += (hours > 11 ? hours - 12 : hours) + '';
		str += minutes + '';
		str += seconds + '';

		var start = new Date(now.getFullYear(), 0, 0),
		diff = now - start,
		oneDay = 1000 * 60 * 60 * 24,
		day = Math.floor(diff / oneDay); // day has remaining days til end of the year
		str += day * (hours > 11 ? 2 : 1);

		var ml = (now.getMilliseconds() / 100) | 0;
		str += ml;

		return str | 0;
	})();
		
	// create launcher intent
	var ntfId = Ti.App.Properties.getInt('ntfId', 0),
	launcherIntent = Ti.Android.createIntent({
		className: 'net.iamyellow.gcmjs.GcmjsActivity',
		action: 'action' + ntfId, // we need an action identifier to be able to track click on notifications
		packageName: Ti.App.id,
		flags: Ti.Android.FLAG_ACTIVITY_NEW_TASK | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
	});
	launcherIntent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
	launcherIntent.putExtra("ntfId", ntfId);

	// increase notification id
	ntfId += 1;
	Ti.App.Properties.setInt('ntfId', ntfId);

	// create notification
	var pintent = Ti.Android.createPendingIntent({
		intent: launcherIntent
	}),
	notification = Ti.Android.createNotification({
		contentIntent: pintent,
		contentTitle: title,
		contentText: message,
		tickerText: statusBarMessage,
		icon: Ti.App.Android.R.drawable.appicon,
		flags: Ti.Android.FLAG_AUTO_CANCEL | Ti.Android.FLAG_SHOW_LIGHTS
	});
	Ti.Android.NotificationManager.notify(notificationId, notification);

	service.stop();

})(Ti.Android.currentService);

app/assets/android/gcm_activity.js

/*global Ti: true, require: true */

(function (activity, gcm) {

	var intent = activity.intent;

	// HERE we catch the intent extras of our notifications
	if (intent.hasExtra('ntfId')) {
		// and then we'll use 'data' property to pass info to the app (see pendingData lines of the 1st snippet)
		gcm.data = {
			ntfId: intent.getIntExtra('ntfId', 0)
		};
	}
	
	// 'isLauncherActivity' is a module property which tell us if the app is not running
	if (gcm.isLauncherActivity) {
		// if the app is not running, we need to start our app launcher activity
		// (launcher activity shows the splash screen and setup your app environment, so we need this)
		var mainActivityIntent = Ti.Android.createIntent({
			// 'mainActivityClassName' is another module property with name of our app launcher activity
			className: gcm.mainActivityClassName,
			packageName: Ti.App.id,
			flags : Ti.Android.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | Ti.Android.FLAG_ACTIVITY_SINGLE_TOP
		});	
		mainActivityIntent.addCategory(Ti.Android.CATEGORY_LAUNCHER);
		activity.startActivity(mainActivityIntent);
	}
	else {
		// if the app is running (is being resumed), just finish this activity!
		activity.finish();
	}

})(Ti.Android.currentActivity, require('net.iamyellow.gcmjs'));

titaniumはこのドキュメントのコピペでいけた
GCM push notifications for Titanium (made easy) - i am yellow

参考
Getting Started | Android Developers
GCM push notifications for Titanium (made easy) - i am yellow
http://freestyle.nvo.jp/archives/1218
spacialdb/gcm · GitHub

selenium-webdriverでwebページ読み込み待ち

テストではうまく動いてたので気づくの遅れてしまった、これだとページ読み込み中にパース処理が走ってしまう

require "selenium-webdriver"
require "nokogiri"

driver = Selenium::WebDriver.for :chrome
driver.navigate.to url
doc = Nokogiri::HTML.parse(driver.page_source)
doc.css('div.content').each do |n|
  ...
end

ので、欲しい要素が表示されるまでwait

require "selenium-webdriver"
require "nokogiri"

driver = Selenium::WebDriver.for :chrome
driver.navigate.to url
wait = Selenium::WebDriver::Wait.new(:timeout => 3) # second
wait.until { driver.find_element(:id => "data").displayed? }
doc = Nokogiri::HTML.parse(driver.page_source)
doc.css('div.content').each do |n|
  ...
end

いけたー

参考
RubyBindings - selenium - Ruby bindings - Browser automation framework - Google Project Hosting

titanium sdk 3.3.0 + androidでgoogle mapが表示されない

titaniumでrss系のandroidアプリ作ったし、次はmap系と思い
公式ドキュメントのサンプルそのままに動かすも
http://docs.appcelerator.com/titanium/3.0/#!/guide/Google_Maps_v2_for_Android
http://docs.appcelerator.com/titanium/3.0/#!/api/Modules.Map

The Google Play services resources were not found

というエラーとともに地図が表示されない・・・
google play services sdkちゃんと入れてるのに・・・
google先生に聞いたら
[TIMOB-16510] Android: The Google Play services resources were not found - Appcelerator JIRA

titanium sdk 3.2.1以降?はバグで動かん
titanium sdk 3.4.0では直ってるよ

どうりで何度やってもmap表示されないわけだ・・・
ちなみにtitanium sdkを3.2.0にしてビルドしようとするとビルドがうまくいかない・・・
titanium clsのバージョンが3.3.0だから?
ということで3.4.0が9月に出るみたいなのでそれまで待ちます

titaniumでandroidアプリ作ってみた

titaniumでandroidアプリ作ってみた
今日のアニメ(アニメニュース&アニメ番組表) - Google Play の Android アプリ
本当は、ionicでios7風のアプリ作りたかったんですが、
http://ionicframework.com
実機で動かしたらもっさりしすぎて使い物にならなかった(ボタン押してから5秒とか10秒とかのタイムラグ)のでやめてtitaniumにした

作りはすごく単純で、alloyrssサンプルをベースに
https://github.com/appcelerator/alloy/tree/master/samples/rss
googleニュースrss使ってアニメのニュースを
しょぼいカレンダー様より番組表のデータを
データを取得する方法 - しょぼいカレンダーのヘルプ
それぞれ表示してるだけ
あと番組開始5分前に通知する機能つけた
通知機能はモジュール使った
benbahrenburg/benCoding.AlarmManager · GitHub
アイコンは、イラストレーターで絵文字フォントをでっかくしただけw
もしよかったら使ってみてください

gemfileのバージョン指定

gemfileのバージョン指定がいつも分らなくなるのでまとめ

0.2.0バージョン固定
gem "dmm-api","0.2.0"
0.2.0以降の最新バージョン
gem "dmm-api",">=0.2.0"
0.2.0以降かつ0.3未満バージョン
gem "dmm-api","~>0.2.0"
最新バージョン
gem "dmm-api"

titanium alloy アプリ名の日本語化 by android

alloy登場以前にはやったことあったけど忘れてたので
まずフォルダとファイル作成

/app
/i18n
  /en
    string.xml
  /ja
    string.xml
/platform
  /android
    AndroidManifest.xml

ファイルの中身はそれぞれ
/i18n/en/string.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="app_name">testapp</string>
</resources>

/i18n/ja/string.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string name="app_name">テストアプリ</string>
</resources>

AndroidManifest.xml
/build/android/AndroidManifest.xmlからコピーして
android:labelを以下のように修正

android:label="@string/app_name"

iosの場合は違うみたい・・・

参考
AOILab Blog Alloy Frameworkの国際化対応

titaniumのアイコンとスプラッシュ画面簡単作成

titaniumに必要な画像ファイルはイラストレータで一個づつ作ってたんですけど
TiCons - Generate all icon & splash screens for your Titanium app from just 2 or 3 files!
Ticonsっていうサービス使ったら、ファイル3つ作るだけでいけた
もっとはやく知りたかった・・・