quarta-feira, 30 de janeiro de 2008

Tela cheia em flash no Globo Vídeos Player

Subiu agora pouco uma nova versão do Globo Vídeos contendo diversas melhorias e algumas correções pontuais. A maioria dessas alterações poucos irão perceber, pois são problemas que somente o controle de qualidade interno tem a paciência de procurar. Mais uma das melhorias sem duvida será muito bem notada, o botão de tela cheia.

Na versão anterior, que usava Windows Media Player, a funcionalidade de tela cheia existia para os usuários de Internet Explorer. Quando entrou o player em flash ela foi removida. Portanto muitos usuários reclamaram de sua remoção.

Acontece que fazer o full screen no flash não é uma tarefa trivial devido as diversas versões de flash que teríamos que suportar. Assim preferimos implementar uma versão mínima do player antes do Big Brother, para posteriormente implementar essas pequenas melhorias. Se tivessemos optado por lançar já com um player completo, talvez só pudéssemos entregá-lo ao final do Big Brother.

Eu particularmente acredito que tomamos a decisão correta. Apesar de termos perdido por um mês a funcionalidade de tela cheia, milhares de usuários que não conseguiam ver vídeos, por causa da M que é o Windows Media Player, agora conseguem. E melhor ainda, agora também têm o botãozinho de tela cheia.

segunda-feira, 14 de janeiro de 2008

Repassando parâmetros rest para funções que esperam parâmetros rest

No ActionScript 3 existe um parâmetro especial, comumente chamando de parâmetro rest, que permite que uma função receba "infinitos" parâmetros. O Java, apartir da versão 5, também possui essa facilidade, que é chamada de varargs. Veja abaixo o exemplo de uma função e o logo abaixo o uso dela:
//Funcao que mostra todos os parametros
public function mostra(... numeros) {
if( numeros && numeros.length > 0 ) {
for each( var i in numeros ) {
trace(i);
}
}
}
//Uso da funcao:
mostra(1);
mostra(1,2,3);
mostra(1,2,3,5,8);
Apesar de ser um recurso muito bom, a forma como ela foi implementada no ActionScript nos traz um problema no momento em que precisamos repassar esses parâmetros para uma outra função que também receba parâmetros opcionais rest.

Isso acontece porque quando a primeira função é chamada, ela é informada com os parâmetros separados por vírgula, mas ela os recebe como um array. Então se você enviar este array para o segundo método, ele será considerado apenas um parâmetro. No Java há um tratamento mais esperto, de forma que quando enviado um array o compilador não permite que mais nenhum parâmetro seja colocado na chamada à função.

Para ilustrar melhor vamos pensar num cenário que mostrei no post passado sobre como testar funções que executem javascript. No caso era uma função proxy para isolarmos a execução de ExternalInterface.call:
public function executarJavaScript(funcao:String, ... parametros) {
try {
return ExternalInterface.call(funcao,parametros);
} catch(erro:Error) {
return null;
}
}
No caso, a função call de ExternalInterface também espera receber parâmetros opcionais, mas como estamos mandando um array, ele achará que estamos mandando apenas dois parâmetros, um com o nome da função javascript e outro com um array. No Google é fácil encontrar uma solução POG e muito feia para isso. Nada mais é que uma cadeia megazord de ifs:
public function executarJavaScript(funcao:String, ... p) {
try {
if( p.length == 0 ) return ExternalInterface.call(funcao,null);
else if( p.length == 1 ) return ExternalInterface.call(funcao,p[0]);
else if( p.length == 2 ) return ExternalInterface.call(funcao,p[0],p[1]);
else if( p.length == 3 ) return ExternalInterface.call(funcao,p[0],p[1],p[2]);
else if( p.length == 4 ) return ExternalInterface.call(funcao,p[0],p[1],p[2],p[3]);
} catch(erro:Error) {
return null;
}
}
Ô coisa feia!

A solução mais elegante para esse problema é utilizar os recurso de reflexão do ActionScript. Ou seja, vamos utilizar o método apply da classe Function, que aguarda os parâmetros como um array, exatamente como recebemos na função ao utilizar um parâmetro rest. Veja como ficaria:
public function executarJavaScript(funcao:String, ... parametros) {
try {
var f:Function = ExternalInterface.call;
var parametrosEnviar:Array = new Array(funcao);
parametrosEnviar = parametrosEnviar.concat(parametros);
return f.apply(null,parametrosEnviar);
} catch(erro:Error) {
return null;
}
}
No caso, concatenamos num array de parâmetros a serem enviados a função e os parâmetros rest. Ao enviá-los pela função apply eles são enviados como se fossem diversos parâmetros separados por vírgula e o método call os recebe corretamente como parâmetros rest.

domingo, 13 de janeiro de 2008

Como testar funções que fazem chamadas javascript no actionscript?

No ActionScript é possível chamar funções javascript utilizando a função ExternalInterface.call. O problema é que essa função é estática, dificultando muito a criação de testes unitários para as funções que necessitam acessar esses recursos externos.

Para fazer isso é preciso isolar a chamada ao javascript em uma função, e no teste unitário substituir essa função por uma outra que apenas a substitua por uma comportamento esperado de acordo com o teste. Vamos ver como fazer isso:

Primeiro isolamos a chamada a ExternalInterface.call em um função que serviria apenas como proxy. Em linguagens menos dinâmicas precisaríamos isolar essa função em uma outra classe pois não seria possível substitui-la posteriormente:
public function executarJavaScript(funcao:String,parametros:Object = null) {
try {
return ExternalInterface.call(funcao,parametros);
} catch(erro:Error) {
return null;
}
}
A classe então que possui essa função precisa ser dinâmica, ou seja deve ter um modificador dynamic, e possuir um atributo da classe Function que recebe no construtor essa função que foi criada:
dynamic public class MinhaClasse {
public var js:Function;
function MinhaClasse() {
js = executarJavaScript;
}
public function executarJavaScript(funcao:String,parametros:Object = null) {
//Conteudo da função já exibido acima :P
}
//Outras funções
}
As demais funções da classe deverão usar então o atributo js como uma função para executar o recurso externo desejado. Veja um exemplo que recupera do javascript a URL onde o swf está sendo exibido:
public function obterUrlAtual():String {
try {
var objJs = js("document.location.href.toString");
if( !objJs ) objJs = js("document.location.toString");
if( !objJs ) return null;
return new String(objJs);
} catch(e:Error) {
return null;
}
}
Para criar um teste unitário com o ASUnit para esta função de exemplo basta configurar de diversas formas o atributo js da classe MinhaClasse de acordo com o comportamento que você deseja testar. Um exemplo é exibido abaixo:
public function testSegundFuncaoJavaScriptEhChamada():void {
var m:MinhaClasse = new MinhaClasse();
m.js = function(funcao:String,parametros:Object = null) {
if(funcao=="document.location.toString") {
return "http://programandosemcafeina.blogspot.com";
} else {
return null;
}
}
var s:String = m.obterUrlAtual();
assertEquals("Quando a primeira função retorna null a segunda função deve ser chamada","http://programandosemcafeina.blogspot.com",s);
}

segunda-feira, 7 de janeiro de 2008

Checar versão do flash de dentro do SWF

Descobrir a versão do flash por javascript é moleza. Uma rápida busca no google lhe traz dezenas de script prontos pra usar. Mas verificar a versão do flash de dentro do SWF foi díficil de encontrar. Até é bem simples. Basta usar o atributo estático version da classe flash.system.Capabilities. Assim:
trace("versao: " + Capabilities.version);
Simples, porém foi díficil encontrar. Como esse código só funciona no Action Script 3, fica parecendo que ele não tem muita utilidade. Mas acontece que existem pequenas diferenças entre as chamadas minor revisions que precisam ser verificadas no projeto em que estou trabalhando.

quarta-feira, 2 de janeiro de 2008

Redirecionar requisições do Apache para uma imagem

Para redirecionar todas as requisições do apache para uma determinada imagem basta utilizar o módulo mod_rewrite.
Se você ainda não utiliza este módulo do apache, é preciso descomentar no arquivo httpd.conf o carregamento dele:
LoadModule rewrite_module modules/mod_rewrite.so
Em seguida deve-se ativar o módulo e criar uma regra, também alterando o mesmo arquivo:
RewriteEngine On
RewriteRule /* /empty.gif
Neste caso o comando RewriteRule informa que todas as requisições devem ser atendidas pelo recurso /empty.gif.