«

»

2月 28

AIR for AndroidでTwitterクライアント

このエントリーを含むはてなブックマークはてなブックマーク - AIR for AndroidでTwitterクライアント Googleブックマークに追加 このエントリをつぶやくこのWebページのtweets

AIR for AndroidでTwitterへつぶやく、ホームタイムライン取得のサンプルを作ってみました。


使用しているライブラリ
as3httpclientlib
oauth-as3
asc3orelib

こんな感じ。アインコンと名前は黒く塗りつぶしてあるだけです。


ソースコード
OAuth認証、ツイート、タイムライン取得を行っているクラス

package
{
	import com.adobe.net.URI;
	
	import flash.events.*;
	import flash.net.*;
	import flash.system.Security;
	import flash.utils.*;
	
	import org.httpclient.*;
	import org.httpclient.events.*;
	import org.httpclient.http.*;
	import org.iotashan.oauth.*;

	
	public class TwiLib extends EventDispatcher
	{
		/**
		 * アクセストークンが既に準備できているかどうか
		 */
		public var ok:Boolean;
		
		//タイムライン取得などに使うクライアント
		private var getInfoClient:HttpClient = new HttpClient();
		//通信中かどうか
		private var getInfoNow:Boolean = false;
		
		//コンシューマートークン
		private var consumerToken:OAuthConsumer;
		//リクエストトークン
		private var requestToken:OAuthToken;
		//アクセストークン
		private var accessToken:OAuthToken;
		//結果のObject
		public var result:Object;
		//ホームタイムラインのjsonデータを格納する変数
		private var homeTimelineData:String;
		
		/**
		 * リクエストトークン取得完了時に実行される関数
		 */
		public var onLoadRequestTokenComplete:Function = null;
		/**
		 * リクエストトークン取得時に通信が正常に終了しなかった場合に実行される関数
		 */
		public var onLoadRequestTokenError:Function = null;
		/**
		 * アクセストークン取得完了時に実行される関数
		 */
		public var onLoadAccessTokenComplete:Function = null;
		/**
		 * アクセストークン取得時に通信が正常に終了しなかった、もしくはPinが間違っている場合に実行される関数
		 */
		public var onLoadAccessTokenError:Function = null;
		/**
		 * ツイート正常完了時に実行される関数
		 */
		public var onTweetComplete:Function = null;
		/**
		 * ツイート失敗時に実行される関数
		 */
		public var onTweetError:Function = null;
		/**
		 * ホームタイムライン取得正常完了時に実行される関数
		 */
		public var onLoadHomeTimelineComplete:Function = null;
		/**
		 * ホームタイムライン取得失敗時に実行される関数
		 */
		public var onLoadHomeTimelineError:Function = null;
		
		public function TwiLib(consumerKey:String, consumerSecret:String)
		{
			consumerToken = new OAuthConsumer(consumerKey, consumerSecret);
			var at:String = getAccessToken();
			var ats:String = getAccessTokenSecret();
			if(at != null && ats != null){
				ok = true;
				accessToken = new OAuthToken(at, ats);
			}
		}
		
		//リクエストトークンを取得するメソッド
		public function loadRequestToken():void{
			var or:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET, "http://twitter.com/oauth/request_token", null, consumerToken);
			var url:String = or.buildRequest(new OAuthSignatureMethod_HMAC_SHA1());
			var loader:URLLoader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.VARIABLES;
			loader.addEventListener(Event.COMPLETE, _onLoadRequestTokenComplete);
			loader.addEventListener(IOErrorEvent.IO_ERROR, _onLoadRequestTokenError);
			loader.load(new URLRequest(url));
		}
		
		private function _onLoadRequestTokenError(e:IOErrorEvent):void{
			var loader:URLLoader = e.target as URLLoader;
			loader.removeEventListener(Event.COMPLETE, _onLoadRequestTokenComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, _onLoadRequestTokenError);
			if(onLoadRequestTokenError != null) onLoadRequestTokenError.apply();
		}
		
		private function _onLoadRequestTokenComplete(e:Event):void{
			var loader:URLLoader = e.target as URLLoader;
			requestToken = new OAuthToken(loader.data.oauth_token, loader.data.oauth_token_secret);
			loader.removeEventListener(Event.COMPLETE, _onLoadRequestTokenComplete);
			if(onLoadRequestTokenComplete != null)onLoadRequestTokenComplete.apply();
		}
		
		/**
		 * ピンコードを取得するURLを開くメソッド
		 */
		public function navigatePinCodeURL():void{
			navigateToURL(new URLRequest("https://twitter.com/oauth/authorize?oauth_token="+requestToken.key));
		}
		
		/**
		 * AccessTokenを取得するメソッド
		 */
		public function loadAccessToken(pinCode:String):void{
			var request:OAuthRequest = new OAuthRequest("GET", "https://api.twitter.com/oauth/access_token", {oauth_verifier:pinCode, oauth_version: "1.0"}, consumerToken, requestToken);
			var url:String = request.buildRequest(new OAuthSignatureMethod_HMAC_SHA1());
			var loader:URLLoader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.TEXT;
			loader.addEventListener(Event.COMPLETE, _onLoadAccessTokenComplete);
			loader.addEventListener(IOErrorEvent.IO_ERROR, _onLoadAccessTokenError);
			var urlReq:URLRequest = new URLRequest(url);
			loader.load(new URLRequest(url));
		}
		
		private function _onLoadAccessTokenError(e:IOErrorEvent):void{
			var loader:URLLoader = e.target as URLLoader;
			loader.removeEventListener(Event.COMPLETE, _onLoadAccessTokenComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, _onLoadAccessTokenError);
			if(onLoadAccessTokenError != null) onLoadAccessTokenError.apply();
		}
		
		private function _onLoadAccessTokenComplete(e:Event):void{
			var loader:URLLoader = e.target as URLLoader;
			var variable:URLVariables = new URLVariables(loader.data as String);
			accessToken = new OAuthToken(variable.oauth_token, variable.oauth_token_secret);
			saveAccessToken(accessToken.key);
			saveAccessTokenSecret(accessToken.secret);
			loader.removeEventListener(Event.COMPLETE, _onLoadAccessTokenComplete);
			loader.removeEventListener(IOErrorEvent.IO_ERROR, _onLoadAccessTokenError);
			if(onLoadAccessTokenComplete != null)onLoadAccessTokenComplete.apply();
		}
		
		/**
		 * Tweetをする関数
		 */
		public function tweet(text:String):void{
			var oauthReq:OAuthRequest = new OAuthRequest(
				"POST", "http://api.twitter.com/1/statuses/update.xml", 
				{ oauth_version:"1.0", status:text},
				consumerToken, 
				accessToken
			);
			var auth:String = oauthReq.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_POST);
			auth = auth.split("&").join("\",");
			auth = auth.split("=").join("=\"");
			auth = auth.replace(/,status=.*/, "");
			var authHeader:URLRequestHeader = new URLRequestHeader("Authorization", "OAuth " + auth);
			
			var req:HttpRequest = new Post();
			req.addHeader("Authorization", authHeader.value);
			req.setFormData([{ name: "status", value:text }]);
			
			var client:HttpClient = new HttpClient();
			client.listener.onComplete = _onTweetComplete;
			client.listener.onError = _onTweetError;
			client.request(new URI("http://api.twitter.com/1/statuses/update.xml"), req);
		}
		
		private function _onTweetComplete(e:HttpResponseEvent):void{
			if(onTweetComplete != null) onTweetComplete.apply();
		}
		
		private function _onTweetError(e:HttpErrorEvent):void{
			if(onTweetError != null) onTweetError.apply();
		}
		
		/**
		 * ホームライムラインの取得
		 */
		public function loadHomeTimeline(parameter:Object=null):void{
			if(getInfoNow) return;
			getInfoNow = true;
			homeTimelineData = "";
			getInfoClient.listener.onError = _onLoadHomeTimelineError;
			getInfoClient.listener.onData = _onLoadHomeTimelineData;
			getInfoClient.listener.onComplete = _onLoadHomeTimelineComplete;
			
			var oauthReq:OAuthRequest = new OAuthRequest(
				"GET", "http://api.twitter.com/1/statuses/home_timeline.json", 
				parameter,
				consumerToken, 
				accessToken
			);
			var auth:String = oauthReq.buildRequest(new OAuthSignatureMethod_HMAC_SHA1(), OAuthRequest.RESULT_TYPE_POST);
			auth = auth.split("&").join("\",");
			auth = auth.split("=").join("=\"");
			auth = auth.concat("\"");
			var authHeader:URLRequestHeader = new URLRequestHeader("Authorization", "OAuth " + auth);
			var req:HttpRequest = new Get();
			req.addHeader("Authorization", authHeader.value);
			
			
			var url:String = "";
			for (var s:String in parameter){
				if(s != "oauth_signature_method" && s != "oauth_token" && s != "oauth_consumer_key" && s != "oauth_nonce" && s != "oauth_signature" && s != "oauth_timestamp")
					url += s+"="+parameter[s]+"&";
			}
			if(url.length > 0){
				url = "?"+url;
				url = url.substr(0, url.length-1);
			}
			getInfoClient.request(new URI("http://api.twitter.com/1/statuses/home_timeline.json"+url), req);
		}
		
		private function _onLoadHomeTimelineError(e:HttpErrorEvent):void{
			if(onLoadHomeTimelineError != null) onLoadHomeTimelineError.apply();
		}
		private function _onLoadHomeTimelineData(e:HttpDataEvent):void{
			homeTimelineData += e.readUTFBytes();
		}
		
		private function _onLoadHomeTimelineComplete(e:HttpResponseEvent):void{
			result = JSON.parse(homeTimelineData);
			getInfoNow = false;
			if(onLoadHomeTimelineComplete != null) onLoadHomeTimelineComplete.apply();
		}
		
		/**
		 * 保存されているアクセストークン取得するメソッド
		 */
		public function getAccessToken():String{
			var obj:SharedObject = SharedObject.getLocal("accessToken");
			if(obj.data.accessToken == undefined) return null;
			else return obj.data.accessToken;
		}
		
		//アクセストークンを保存するメソッド
		private function saveAccessToken(code:String):void{
			var obj:SharedObject = SharedObject.getLocal("accessToken");
			obj.data.accessToken = code;
		}
		
		/**
		 * 保存されているアクセストークンSecret取得するメソッド
		 */
		public function getAccessTokenSecret():String{
			var obj:SharedObject = SharedObject.getLocal("accessTokenSecret");
			if(obj.data.accessTokenSecret == undefined) return null;
			else return obj.data.accessTokenSecret;
		}
		
		//アクセストークンSecretを保存するメソッド
		public function saveAccessTokenSecret(code:String):void{
			var obj:SharedObject = SharedObject.getLocal("accessTokenSecret");
			obj.data.accessTokenSecret = code;
		}
	}
}

認証、ツイート、タイムライン各画面

<?xml version="1.0" encoding="utf-8"?>
<s:ViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
							xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.TimelineView" applicationDPI="160"
							initialize="init(event)">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			
			public static var twiLib:TwiLib;
			private const CONSUMER_KEY:String = "";
			private const CONSUMER_SECRET:String = "";
			
			protected function init(event:FlexEvent):void
			{
				twiLib = new TwiLib(CONSUMER_KEY, CONSUMER_SECRET);
			}
			
		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
	</fx:Declarations>
</s:ViewNavigatorApplication>

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="TimelineView" initialize="init(event)" xmlns:local="*">
	<fx:Script>
		<![CDATA[
			import mx.collections.ArrayCollection;
			import mx.events.FlexEvent;
			private var twiLib:TwiLib;
			
			protected function init(event:FlexEvent):void
			{
				twiLib = TwitterClient.twiLib;
				twiLib.onLoadHomeTimelineComplete = loadTimelineComplete;
			}
			
			protected function updateTimeline(event:MouseEvent):void
			{
				twiLib.loadHomeTimeline();
			}
			
			private function loadTimelineComplete():void{
				dataArray = new ArrayCollection(twiLib.result as Array);
				for(var i:int = 0; i < dataArray.length; i++){
					dataArray[i].name = dataArray[i].user.name;
					dataArray[i].image = dataArray[i].user.profile_image_url_https;
				}
			}
			
		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
		<s:ArrayCollection id="dataArray"/>
	</fx:Declarations>
	<s:Button x="9" y="410" label="Tweet" click="navigator.pushView(TweetView)"/>
	<s:Button x="84" y="410" label="認証" click="navigator.pushView(TwitterClientHomeView)"/>
	<s:List x="10" y="10" width="300" height="390" id="timeline_list" dataProvider="{dataArray}" >
		<s:itemRenderer>
			<fx:Component>
				<s:IconItemRenderer labelField="name" iconField="image" iconWidth="50" iconHeight="50" messageField="text" selectionColor="0x0033660" color="0x0066FF"/>
			</fx:Component>
		</s:itemRenderer>
	</s:List>
	<s:Button x="247" y="410" label="更新" click="updateTimeline(event)"/>
</s:View>

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="TweetView">
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
	</fx:Declarations>
	<s:TextInput x="10" y="60" height="216"/>
	<s:Button x="255" y="310" label="送信"/>
	<s:Button x="256" y="413" label="戻る" click="navigator.popView()"/>
</s:View>

<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" title="認証" initialize="init(event)">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			
			import org.iotashan.oauth.*;
			
			private var twiLib:TwiLib;
			
			protected function init(event:FlexEvent):void
			{
				twiLib = TwitterClient.twiLib;
				if(twiLib.ok){
					status_lbl.text = "認証済み";
				}
				twiLib.onLoadRequestTokenComplete = onRequestTokenComplete;
				twiLib.onLoadRequestTokenError = onRequestTokenError;
				twiLib.loadRequestToken();
			}
			
			private function onRequestTokenComplete():void{
				pin_get_btn.enabled = true;
				status_txt.text = "Request Token OK";
			}
			
			private function onRequestTokenError():void{
				status_txt.text = "Request Token Error";
			}
			
			protected function pinOk(event:MouseEvent):void
			{
				twiLib.onLoadAccessTokenComplete = onAccessTokenComplete;
				twiLib.onLoadAccessTokenError = onAccessTokenError;
				twiLib.loadAccessToken(pin_txt.text);
			}
			
			private function onAccessTokenError():void{
				status_txt.text = "Access Token Error";
			}
			
			private function onAccessTokenComplete():void{
				status_txt.text = "Access Token OK";
				status_lbl.text = "認証済み";
			}
			
		]]>
	</fx:Script>
	<fx:Declarations>
		<!-- 非ビジュアルエレメント (サービス、値オブジェクトなど) をここに配置 -->
	</fx:Declarations>
	<s:Button x="100" y="129" id="pin_get_btn" label="PinCode取得" click="twiLib.navigatePinCodeURL()"/>
	<s:TextInput x="10" y="261" id="pin_txt"/>
	<s:Label x="55" y="221" text="Pin Codeを入力してください"/>
	<s:Button x="253" y="304" label="送信" click="pinOk(event)"/>
	<s:Label x="10" y="10" text="未認証" id="status_lbl"/>
	<s:Label x="12" y="378" text="" id="status_txt"/>
	<s:Button x="256" y="409" label="戻る" click="navigator.popView()"/>
</s:View>

2 comments

  1. c-cat

    Twitter API 1.1 に対応していますか?

    1. matsu4512

      Twitter API 1.1が出るより前に作ったものなのでおそらく対応していません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次の HTMLタグおよび属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>