From the monthly archives:

April 2010

Myšlenky nad myšlenkami Steva Jobse

by Robin on April 29, 2010

Steve Jobs and iPadSteve Jobs odpovída na emaily, píše blog a už mu chybí jenom účet na socilální síti Twitter. Před chvílí se na stránkach Apple objevil článek o tom co si myslí o Adobe Flash pod kterým je podepsaný on sám.

Celý článek celkem pěkne věcně shrnuje jeho obavy a výtky okolo Flashe na mobilních zařízeních.

Doporučuji si přečíst originál. Shrnul jsem několik nejvýznamnějších myšlenek do následujících bodů a na konci připojuji i svůj názor.

Na začátek krásná citace:

Flash was created during the PC era – for PCs and mice. Flash is a successful business for Adobe, and we can understand why they want to push it beyond PCs. But the mobile era is about low power devices, touch interfaces and open web standards – all areas where Flash falls short.

  • Adobe a Apple spolu dříve dobře vycházeli a měli hodne společného — teď už jejich společných zájmů moc není
  • Flash je proprietální a uzavřený systém
  • Apple sám tvoří otevřené standardy a těch se hodlá držet, Flash jím není
  • skoro každý mobilní webový přohlížeč používá WebKit; výjimkou je jen Microsoft
  • Adobe říká že 75% videa na webu je Flash — už neříká že všechno video je většinou také dostupné v moderním formátu H.264, které lze přehrát na iPhone, iPod touch či iPad
  • YouTube které se rovná cca 40% videa na Webu je dostupné jako nativní aplikace na iPhone OS
  • Symantec ohlásil že Flashmá nejhoší bezpečnostní “skóre” za rok 2009
  • Flash nefunguje dobře na mobilních zařízeních
  • Adobe mělo představit mobilní Flash již začátku roku 2009 ale stále se to odkládá
  • H.264 videa je možné přehrávat až 10 hodin na iPhone přičemž softwarově enkodovaná videa (Flash) jen 5 hodin; tím Flash zabíjí výdrž baterie
  • Flash není optimalizovaný pro moderní dotykové displaye ale pro myš a to je minulost
  • Adobe by se mělo zaměřit na vývoj nástrojů pro HTML5 projektů místo toho aby kritizovalo Apple

Mé shrnutí

Myslím že cíl Adobe není pomoct vývojářům výtvářet nejlepší aplikace pro iPhone OS ale pouze jim umožnit vytváře druholigové cross-platform projekty, které stejně nikdy nebudou úspěšné protože nativní vzhled je prostě nativní vzhled. I kdyby Apple povolil Flash pro iPhone OS, tak by se stejně většina webů a aplikací musela předělat protože by nebyly optimalizované pro ovládání prsty a gesty… Kdo z vývojářů by to dělal? Takový oser.

Flash je mrtvý a s každým prodáným iPhonem, iPadem i iPod touchem přichází o další procenta své penetrace.

Má někdo nějaký kvalitní argument, který by dokázal myšlenky a argumenty v tomto článku nějak vyvrátit? Podělte se o ně v komentářích pod článkem nebo mi napište veřejně na Twitteru.

SubjectTransformator To Transform Any DisplayObject

by josefchutka on April 26, 2010

SubjectTransformator class is a simple utility class that lets you transform (rotate, scale, drag) any DisplayObject on stage. As for now, this class may be the subject of change, so please download latest version before use. You can see the demo below in the article…

Usage is pretty simple:

addChild(image); // Bitmap, Loader, whatever...

var transformator:SubjectTransformator = new SubjectTransformator();
addChild(transformator);
transformator.subject = image;
transformator.draggable = true;
transformator.addResizer(iconResize1, SubjectTransformator.POSITION_LT);
transformator.addResizer(iconResize2, SubjectTransformator.POSITION_RB);
transformator.addRotator(iconRotation1, SubjectTransformator.POSITION_RT);
transformator.addRotator(iconRotation2, SubjectTransformator.POSITION_LB);
transformator.transformatorMatchSubject();

Demo (click link below to get the source code):

The Ultimate Debugging And Profiling

by josefchutka on April 22, 2010

Yesterday I came across interesting articles from jpauclair. The first one is about Almighty PreloadSWF (mm.cfg attribute to define preloader for any as3 .swf file) and the second about his new Flash Visual Profiler. Long story short, you can debug and profile any ActionScript 3 compiled .swf file on the web (or local) you decide to, for example in De MonsterDebugger (open source debugger for Adobe Flash, Flex and AIR).

You can read more about how it works on articles linked in article perex, I am gonna supply just the quick todo list to make this work. Here it is:

  • install Debug Player (if you do not have one already)
  • install De MonsterDebugger and run it
  • download MicroProfiler.swf to your local drive
  • make sure the download location (etc. d:/whatever/MicroProfiler.swf) is within local trusted scope
  • open your mm.cfg (on win XP C:\Documents and Settings\username\mm.cfg) file and add this line
    PreloadSWF=c:\whatever\MicroProfiler.swf
  • close all tabs using flash player or just restart browser

Thats it, thats all. From now on, whenever you visit any site with .swf file compiled in as3, you will see a small MicroProfiler over flash movie. If De MonsterDebugger runs it receives data, it also allows you to change variable values and call functions on the fly. Just cool.

Facebook now with OAuth and Open Graph (update)

by josefchutka on April 22, 2010

Few days before while I was working on TwitterLogger Class for ActionScript 3 I discovered OAuth – an open protocol to allow secure API authorization . While reading all the stuff about OAuth and PHP SDKs, I noticed one statement somewhere (can’t find it nowhere) that Facebook was in fact using OAuth for its Facebook Connect tool but some derived version. This has now changed! Facebook is standardizing communication and authorization by introducing Open Graph and OAtuh 2.0.

F8 is a Facebook conference to bring together the developers and entrepreneurs who are building the social Web by moving fast, taking risks, and hacking traditional systems. In April 21, 2010, in F8 conference a few interesting things have been mentioned about the new Facebook API. Facebook CEO Mark Zuckerberg announced that the Facebook Connect brand would be eliminated as part of the launch of Open Graph.

Update (May 5, 2010): I have created Facebook Graph API & OAuth 2.0 & Flash – FacebookOAuthGraph ActionScript 3 class to use with OAuth 2.0 and Graph

Open Graph

The Open Graph API will allow any page on the Web to have all the features of a Facebook Page – users will be able to become a Fan of the page, it will show up on that user’s profile and in search results, and that page will be able to publish stories to the stream of its fans. (Facebook Roadmap Open Graph API)

Open Graph protocol also introduces <meta> tags, that allows you to specify structured information about your web page when used with like button etc.

Here are some url examples of Graph API usage:

http://graph.facebook.com/[USERID]          - user’s graph for your app
http://graph.facebook.com/[USERID]/friends  - access to user’s friends
http://graph.facebook.com/[USERID]/likes    - access user’s likes

By default these requests return JSON objects, but I guess there is a parameter to be passed to get xml instead (my guess).

Cool thing about this is there is a benevolent crossdomain file on http://graph.facebook.com/crossdomain.xml:

<cross-domain-policy>
    <allow-access-from domain="*"/>
    <site-control permitted-cross-domain-policies="master-only"/>
</cross-domain-policy>

Some of those returns “OAuthAccessTokenException” thats where new OAuth comes in scene.

OAuth 2.0

Second thing noticed was that the company is standardizing the authorization via the OAuth 2.0 standard. The important thing here is, OAuth is already available for all existing Facebook APIs, and there are multiple methods to get authenticated with OAuth in Facebook. Check out the PHP example code example code for authentication on GitHub. Difference to previous connect is that sessions may last longer than 24 hours.

Lets hope there will be new version of Facebook ActionScript API released soon enough with all these new features…

Where to go from here:

Video a můj report z Creative Future 2010

by Robin on April 21, 2010

Včera jsem spolu s dalšími kreativními lidmi navštívil český launch Adobe Creative Suite CS5 [Oficiální video]. Akce to byla velmi povedená, skvěle zorganizovaná a chyběla snad akorát Twitter Wall (což si pro příště snad vezmu na starost). Pěkné shrnutí novinek v balíku CS5 včera sepsal Radek Hulán — tímto mu děkuji, že se nemusím o novinkách tak moc rozepisovat a rád bych to jen krátce shrnul z mého pohledu designéra.

Tom KrchaAugmented Reality from Robin Raszka on Vimeo.

  • CS5 bude v ČR dostupná v ENG na přelomu duben/květen a CZ verze bude následovat do 14ti dnů
  • Nejvíc novinek je pravděpodobně v Illustratoru, ten se posunul opravdu hodně dál (těším se až jej vyzkouším pro návrh Icons a jiný pixel-perfect stuff)
  • Photoshop dostal několik zásadních kouzelných funkcí jako je např. Content-Aware Fill nebo lepší podporu pro maskování
  • Na Fireworks se uplně zapomělo a osobně vubec netuším k čemu tento software je
  • Pro pohodlné využívání budete potřebovat hodně RAM (Mé 4GB v MacBook Pro – Early 2008 opravdu nejsou dostačující např. pro práci s 3D ve Photoshopu)
  • Na mém Macu jsem pocitíl mírné zvýšení výkonu a rychlosti
  • Mini Bridge se hodí ale opět zaleží na výkonu vašeho PC — u mě to není nijak extra svížné a čekal jsem víc novinek v UI samotné Bridge
  • Na konferenci uplně vypadla zmínka o možnosti vývoje aplikací pro iPhone pomocí Flash CS5
  • Flash Catalyst je skvělý nástroj pro výrobu jednoduchých microsite nebo prototypování ale dost se naklikáte
  • Dreamwaver nepřinesl tolik převratných novinek ale spíš integroval několik hojně používaných funkcí, které již dávno webdesignéři znají např. z Firebugu. Zajímavá je také podpora pro lokální vývoj šablon pro redakční systémy. Tady je duležité zmínit ještě jednou, že teď je všechno v jedné aplikaci a nemusíte používat víc různých nástrojů.

Na závěr bych ještě zmínil, že jsem si nevšiml nikoho kdo by akci natáčel na video, což je škoda, protože přednášky byly obsahově celkem přínosné a tak jsem si dovolil natočit několik zajímavých momentů na svůj úžasný iPhone 3GS. Další videa neleznete na mém Vimeu.

Jaké máte pocity z nové CS5? Na co se nejvíc těšíte? Podělte se o svůj názor v diskusi pod článkem!

50-inch Multitouch Plasma Running Adobe AIR

by Tom Krcha on April 20, 2010


Apple Vs Adobe, další pěkné shrnutí

by Robin on April 19, 2010

Patrick Zandl dnes velmi pěkně a věcně shrnul situaci okolo této války o mobilní platformu. Celý článek si můžete přečíst na serveru Lupa.cz a doporučuji se vyhnout diskuzi a udělat si především vlastní názor. S názory Patricka plně souhlasím a jen bych doplnil můj osobní zkracený pohled na věc:

Adobe se svým přístupem nesnaží rozšiřovat a budovat platformu okolo iPhone OS ale za každou cenu být u úspěchu iPhone potažmo iPhone OS a rozšířit penetraci Flashe. Cílem Adobe není podpořit vývojáře při vývoji nativních iPhone OS aplikací ale umožnit jim portovaní svých Flashových her na iPhone OS.

Díky tomuto přistupu Adobe mají Flash vývojáři do budoucna se svým portováním smůlu ale mají na výběr: pokud budou chtít hrát první ligu, budou se prostě muset naučit Objective-C nebo se vrhnout do druhé ligy, která se  hraje o kus dál.

Quick tip: TwitterUtils.makeDate() correction

by josefchutka on April 19, 2010

There is a bug in current version (2.0) of TwitterScript API. In com.twitter.api.utils.TwitterUtils.makeDate() function, there is a wrong month assignment on line 22. While you match date from a String, make sure to subtract 1 from month number, while ActionScript 3 Date.month is zero-based. Issue already submitted on TwitterScript code home page.

While watching Adobe Creative Suite 5 demonstration, I got inspired to create LinkedTextFields Class. In Flash CS5 IDE there is a new function that lets you link text fields in order to split one text into all fields based on how much fits in each (for example article columns). I tought it must be possible to do this with ActionScript 3. And it is. I created this lightweight LinkedTextFields Class that does exact the same thing as the new tool in CS5. LinkedTextFields Class lets you render plain text as well as HTML text. By default, text is splited based on white space characters, but you can define your own delimiters. Long story short, here is the proof of concept:

sk.yoz.text.LinkedTextFields Class.

Update (Apr 15, 2010): improved performance, <br> tag support

package sk.yoz.text
{
    import flash.events.EventDispatcher;
    import flash.text.TextField;

    public class LinkedTextFields extends EventDispatcher
    {
        public static const DEFAULT_TEXT_DELIMITER:RegExp = /(\s)/;
        public static const DEFAULT_HTML_DELIMITER:RegExp = /([\s<>])/;

        public var autoRender:Function = null;
        public var textDelimiter:RegExp;
        public var htmlDelimiter:RegExp;

        protected var list:Array = [];
        protected var _text:String;

        public function LinkedTextFields(autoRender:Function = null,
            delimiter:RegExp = null, htmlDelimiter:RegExp = null)
        {
            super();

            this.autoRender = autoRender;
            this.textDelimiter = textDelimiter
                ? textDelimiter
                : DEFAULT_TEXT_DELIMITER;
            this.htmlDelimiter = htmlDelimiter
                ? htmlDelimiter
                : DEFAULT_HTML_DELIMITER;
        }

        public function set text(value:String):void
        {
            _text = value;
            if(autoRender != null)
                autoRender();
        }

        public function get text():String
        {
            return _text;
        }

        public function add(textField:TextField, index:int = -1):void
        {
            if(index == -1)
                list.push(textField);
            else
                list.splice(index, 0, textField);
            if(autoRender != null)
                autoRender();
        }

        public function remove(textField:TextField):void
        {
            var index:int = list.indexOf(textField);
            if(index != -1)
                list.splice(index, 1);
            if(autoRender != null)
                autoRender();
        }

        public function renderText():void
        {
            emptyTextFields("text");
            if(!this.text)
                return;

            var chunk:String, prevText:String;
            var chunks:Array = text.split(textDelimiter);
            var textField:TextField = list[0];
            var last:Boolean = !nextTextField(textField);

            while(chunks.length)
            {
                chunk = chunks.shift();
                if(chunk == "\r")
                    chunk = "\n";
                prevText = textField.text;
                textField.appendText(chunk);
                if(!last && textField.maxScrollV > 1)
                {
                    textField.text = prevText;
                    textField = nextTextField(textField);
                    textField.text = chunk;
                    if(!nextTextField(textField))
                        last = true;
                }
            }
        }

        public function renderHtmlText():void
        {
            emptyTextFields("htmlText");
            if(!this.text)
                return;

            var chunk:String, text:String = "", prevText:String;
            var chunks:Array = this.text.split(htmlDelimiter);
            var textField:TextField = list[0];
            var last:Boolean = !nextTextField(textField);
            var tag:String = "", tagName:String, isTag:Boolean, tags:Array = [];

            while(chunks.length)
            {
                chunk = chunks.shift();

                if(chunk == "<")
                    isTag = true;

                if(isTag && tag == "<")
                {
                    tagName = chunk.toLowerCase();
                    if(tagName.substr(0, 1) == "/")
                        removeLastTag(tags, tagName.substr(1));
                    else
                        addTag(tags, tagName);
                }

                if(isTag)
                    tag += chunk;

                if(isTag && chunk == ">")
                {
                    isTag = false;
                    if(tag.substr(-2) == "/>" || tagName == "br")
                        removeLastTag(tags, tagName);
                    else if(tag.substr(0, 2) != "</")
                        addLastTagDefinition(tags, tag);
                    chunk = tag;
                    tag = "";
                }

                if(isTag)
                    continue;

                prevText = text;
                text += chunk;
                textField.htmlText = text + writeAllTagClosage(tags);

                if(last || textField.maxScrollV <= 1)
                    continue;

                textField.htmlText = prevText + writeAllTagClosage(tags);
                textField = nextTextField(textField);
                text = writeAllTagDefinitions(tags) + chunk;
                if(!nextTextField(textField))
                    last = true;
            }

            textField.htmlText = text + writeAllTagClosage(tags);

        }

        public function emptyTextFields(type:String = "text"):void
        {
            for each(var textField:TextField in list)
                textField[type == "text" ? type : "htmlText"] = "";
        }

        protected function addTag(list:Array, tagName:String):void
        {
            list.push({name:tagName});
        }

        protected function addLastTagDefinition(list:Array, definition:String)
            :void
        {
            list[list.length - 1].definition = definition;
        }

        protected function writeAllTagDefinitions(list:Array):String
        {
            var definitions:String = "";
            for each(var item:Object in list)
                definitions += item.definition;
            return definitions;
        }

        protected function writeAllTagClosage(list:Array):String
        {
            var closage:String = "";
            for(var i:int = list.length - 1; i >= 0; i--)
                closage += "</" + list[i].name + ">";
            return closage;
        }

        protected function removeLastTag(list:Array, tagName:String):void
        {
            if(list[list.length - 1].name == tagName)
                list.splice(list.length - 1, 1);
        }

        protected function nextTextField(textField:TextField):TextField
        {
            var index:int = list.indexOf(textField);
            if(index == -1 || index + 1 >= list.length)
                return null;
            return list[index + 1];
        }
    }
}

Usage:

package
{
    import flash.display.Sprite;
    import flash.text.TextField;

    import sk.yoz.text.LinkedTextFields;

    [SWF(width="465", height="465", frameRate="30", backgroundColor="#ffffff")]

    public class Linked extends Sprite
    {
        private var link:LinkedTextFields = new LinkedTextFields();

        public static const TEXT:String = "Lorem ipsum dolor sit amet, consec" +
                "tetur adipiscing elit. Vivamus mattis purus ac diam bibendum" +
                " vitae rhoncus sapien posuere.\n\nVestibulum gravida mi vel " +
                "pis cursus sit amet interdum eros egestas. Nunc fermentum ul" +
                "tricies velit, non dictum est venenatis ultricies. Pellentes" +
                "que vehicula lectus nec nibh mollis pellentesque. Aenean vit" +
                "ae tortor lectus. Nulla imperdiet erat nec sapien ornare ut " +
                "laoreet sem venenatis. Mauris ipsum augue, lacinia sed solli" +
                "citudin interdum, rutrum ornare nisl. Nulla interdum lorem a" +
                "ccumsan leo tincidunt adipiscing. Nunc egestas blandit nibh," +
                " ultrices accumsan tortor commodo non. Cras tempus scelerisq" +
                "ue ullamcorper.\n\nNullam velit lacus, facilisis vel pharetr" +
                "a in, lacinia vitae purus. Sed nisl lorem, lacinia a molesti" +
                "e eu, pretium at justo. Nulla facilisi. Maecenas sagittis te" +
                "llus quis sapien vestibulum gravida. Suspendisse potenti. Cu" +
                "rabitur at felis et nisl tincidunt condimentum ut nec eros. " +
                "Pellentesque neque magna, venenatis sit amet bibendum eget, " +
                "cursus eu sapien. Nulla malesuada convallis felis nec congue" +
                ". Sed lorem massa, egestas a pharetra commodo, consectetur i" +
                "mperdiet odio. Nullam nec neque ac metus ultrices commodo. M" +
                "aecenas a lorem sed augue tincidunt tincidunt. Nam lobortis " +
                "vestibulum massa, ut viverra leo venenatis a. Praesent scele" +
                "risque, velit non tempor euismod, tellus nulla aliquam nisi," +
                " vitae vestibulum lectus est vel neque. Class aptent taciti " +
                "sociosqu ad litora torquent per conubia nostra, per inceptos" +
                " himenaeos. Proin eleifend turpis vel lectus vehicula accums" +
                "an. Vestibulum aliquam mi et metus tristique fermentum. Null" +
                "a eget lorem in mi feugiat sollicitudin at quis lacus.";

        public static const HTML:String = "<font size='20'>Lorem ipsum <font " +
                "color='#ff0000'>dolor sit amet</font>, consectetur <font fac" +
                "e='_sans'><b>adipiscing</b> elit. Aliquam ac orci <u>urna, e" +
                "u ornare ue. <b>Nullam <font color='#00ff00'>suscipit, <font" +
                " color='#0000ff'>turpis vitae viverra ultrices</font>, turpi" +
                "s nisl euismod lorem, convallis tristique</font> lectus risu" +
                "s</b> convallis</u> orci. Vitae vestibulum lectus</font> <fo" +
                "nt size='15' color='#999999'>est vel neque. Class aptent tac" +
                "iti <b>sociosqu ad litora <u>torquent</u> per conubia nostra" +
                "</b>, per inceptos eget lorem in mi feugiat sollicitudin at " +
                "quis lacus ultrices accumsan tortor commodo non. Cras tempus" +
                " scelerisq</font></font>";

        public function Linked():void
        {
            super();

            var textField1:TextField = new TextField();
            textField1.x = 10;
            textField1.y = 10;
            textField1.width = 410;
            textField1.height = 100;
            textField1.border = true;
            textField1.wordWrap = true;
            addChild(textField1);

            var textField2:TextField = new TextField();
            textField2.x = 10;
            textField2.y = 120;
            textField2.width = 200;
            textField2.height = 100;
            textField2.border = true;
            textField2.wordWrap = true;
            addChild(textField2);

            var textField3:TextField = new TextField();
            textField3.x = 220;
            textField3.y = 120;
            textField3.width = 200;
            textField3.height = 100;
            textField3.border = true;
            textField3.wordWrap = true;
            addChild(textField3);

            link.add(textField1);
            link.add(textField3);
            link.add(textField2, 1);

            //link.text = TEXT;
            link.text = HTML;
            link.renderHtmlText();
        }
    }
}

You may ask, wheter this is also possible on for example Flex components, and the answer is: YES! Every single component that uses text uses TextField to render it, but there text fields are hidden under internal namespace. For example for TextInput/TextArea components you reach TextField this way:

import mx.core.mx_internal;
use namespace mx_internal;
var tf:TextField = TextField(textAreaId.getTextField());

For the last couple of days I have been working with Twitter API, trying to make the api usable for flash in browser the same way as FacebookLogger (Facebook API extension) is. Guess what, I did it! I created TwitterLogger. TwitterLogger class extends official TwitterScript (ActionScript API) and implements OAuth (and TwitterOAuth – PHP Library to support OAuth for Twitter’s REST API) authorization protocol to gain full access into Twitter API from flash in browser.

TwitterScript already contains full api access but some call requires authorization that brings you into 2 issues:

  • obtain username and password from user – unwanted operation
  • crossdomain authorization – only possible for AIR due to Twitter crossdomain policy restriction

Sets the username and password for this instance, setting the flag to use https to true. Note that this will not work at all in Flash player 9.0.115, and will only work in later versions if the remote server has the <code>allow-http-request-headers-from</code> tag set permissively in its crossdomain policy file. For more information see: http://kb.adobe.com/selfservice/viewContent.do?externalId=kb403184. Unfortunately Twitter has it set to (as of Sept 2008): <allow-http-request-headers-from domain=”*.twitter.com” headers=”*” secure=”true”/> which only lets in the twitter badges originating from twitter.com. Since that’s the case, authentication will only work for AIR.
If you use this for Flash in the browser, it will fail over to the browser’s basic auth without an issue. (described in com.twitter.api.Twitter.setAuthenticationCredentials())

This requires another method on scene. Since Twitter introduced OAuth it is possible to get connection into Twitter API via this open source secure authorization. To communicate with Twitter we gonna use server side proxy. So lets start:

1. Register a new twitter application, these settings are crucial:

Application Website: http://blog.yoz.sk/examples/twitterLogger/
Application Type: Browser
Callback URL: http://blog.yoz.sk/examples/twitterLogger/callback.php
Default Access type: Read & Write
Use Twitter for login: Yes

2. download OAuth.php and twitteroauth.php from TwitterOAuth into /library/ dir

3. create /config.php file containing:

<?php
// fill your own Twitter application Consumer key
define('CONSUMER_KEY', 'qSoF24kOuNCiPCQwWfe0yQ');

// fill your own Twitter application Consumer secret
define('CONSUMER_SECRET', 'xahJ7hpJeevq411N5NQXcTbJ5hQFWDwidtTpsbc');

// fill your Twitter application Callback URL
define('OAUTH_CALLBACK', 'http://blog.yoz.sk/examples/twitterLogger/callback.php');

4. /connect.php file:

<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0");
header("Pragma: no-cache");
header("Last-Modified:".gmdate("D, d M Y H:i:s")." GMT");
header('Content-Type: text/html; charset=utf-8');

session_start();
require_once 'config.php';
require_once 'library/twitteroauth.php';

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET);
$request_token = $connection->getRequestToken(OAUTH_CALLBACK);

$_SESSION['oauth_token'] = $token = $request_token['oauth_token'];
$_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret'];

$url = $connection->getAuthorizeURL($token);
header('Location: ' . $url);
exit;

5. /callback.php

<?php
session_start();
require_once 'config.php';
require_once 'library/twitteroauth.php';

$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION['oauth_token'], $_SESSION['oauth_token_secret']);
$access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']);
$_SESSION['access_token'] = $access_token;
unset($_SESSION['oauth_token']);
unset($_SESSION['oauth_token_secret']);
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="sk" lang="sk" dir="ltr">
<head>
	<script type="text/javascript">
	<!--
		if(window.opener && window.opener.confirmTwitterConnection)
		{
			window.opener.confirmTwitterConnection();
			self.close();
		}
	//-->
	</script>
</head>
<body>
<p>You may now close this window.</p>
</body>
</html>

6. /proxy.php

<?php
session_start();
require_once 'config.php';
require_once 'library/twitteroauth.php';

$access_token = $_SESSION['access_token'];
$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $access_token['oauth_token'], $access_token['oauth_token_secret']);

$parameters = array();
foreach($_POST as $key => $value)
if($key != "method" && $key != "url")
	$parameters[$key] = $value;

echo $connection->OAuthRequest($_POST['url'], $_POST['method'], $parameters);

7. Create new flex project

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
    applicationComplete="init()">
<mx:Script>
<![CDATA[
    import sk.yoz.events.TwitterLoggerEvent;
    import sk.yoz.net.TwitterLogger;

    private var connector:String = "http://blog.yoz.sk/examples/twitterLogger/connect.php";
    private var proxy:String = "http://blog.yoz.sk/examples/twitterLogger/proxy.php";

    [Bindable]
    private var twitter:TwitterLogger = new TwitterLogger(connector, proxy);

    [Bindable]
    private var lastCallResult:String = "";

    private function init():void
    {
        twitter.addEventListener(TwitterLoggerEvent.CALL_COMPLETE, callComplete);
    }

    private function callComplete(event:TwitterLoggerEvent):void
    {
        lastCallResult = event.data.toString();
    }
]]>
</mx:Script>
<mx:HBox>
    <mx:Button label="connect" click="twitter.connect()" />
    <mx:Label text="{twitter.connected ? 'connected' : 'not connected'}" />
</mx:HBox>

<mx:HBox>
    <mx:TextInput id="status" text="hallo world status"/>
    <mx:Button click="twitter.setStatus(status.text)" label="update status"/>
</mx:HBox>

<mx:TextArea text="{lastCallResult}" width="100%" height="100%"/>
</mx:Application>

8. Download TwitterScript classes and make sure to rewrite private to protected namespace for these methods and vars in com.twitter.api.Twitter class:

protected var loaders;
protected function addLoader(...
protected function errorHandler(...

9. Download sk.yoz.net.TwitterLogger, sk.yoz.net.TwitterProxyLoader and sk.yoz.events.TwitterLoggerEvent classes into your flex project under the correct namespace (sk.yoz…)

10. Make sure your index.html wrapper class defines allowScriptAccess and flash id and name, it may look like something like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="cs" xml:lang="cs">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-language" content="cs" />
    <title>TwitterLogger</title>

    <script type="text/javascript" src="js/swfobject.js"></script>
    <script type="text/javascript">
        //<![CDATA[
        var flashvars = {}

        var params = {
            allowScriptAccess: "sameDomain"
        };

        var attributes = {
            id: "sz",
            name: "sz"
        };

        swfobject.embedSWF("App.swf", "alternative", "100%", "100%", "9.0.124",
            "expressInstall.swf", flashvars, params, attributes);
        //]]>
    </script>
    <style>
	<!--
		body {margin:0px;overflow:hidden;}
		html, body, object, embed {width:100%;height:100%;outline:none;}
	-->
    </style>
 </head>
 <body style="text-align:center;">
    <div id="alternative">
        <a href="http://www.adobe.com/go/getflashplayer">
            <img src="http://www.adobe.com/images/shared/download_buttons/get_flash_player.gif" alt="Get Adobe Flash player" />
        </a>
    </div>
</body>
</html>

Publish html template and copy all flash files into your application website. You should now have this structure in there (http://blog.yoz.sk/examples/twitterLogger/)

/js/swfobject.js
/library/OAuth.php
/library/twitteroauth.php
/App.swf
/callback.php
/config.php
/connect.php
/index.html
/proxy.php

If you do same changes, make sure all these files are on same domain because it uses PHP Session to store token and JavaScript between-window communication. Now lets see our application (http://blog.yoz.sk/examples/twitterLogger/):

To update your satus, first click on connect, window popup opens and redirects itself into Twitter asking for permission. After you click allow in popup, popup closes itself and change status in flex near connect button to “connected”. Now you are ready to update your Twitter status. TextArea contains last Twitter appi call reply (after status update). The good thing with OAuth is, it remembers your acception for some time, so you do not have to click allow every time…

There may occur error on Twitter popup saying:

This page is no longer valid. It looks like someone already used the token information you provided. Please return to the site that sent you to this page and try again … it was probably an honest mistake.

I guess it may have something to do with cached request on connect.php, so I added few expire headers into it.

Where to go from here: