Resolvendo o problema do 3G no Brasil: “Russian Roulette algorithm”.

Este blog apresenta agora, pela primeira vez na história, um algoritmo (meramente teórico) para resolver o problema do 3G no Brasil, que como já comentamos algumas vezes aqui neste blog é algo de décimo mundo (todas: Vivo, Oi, Tim e Claro).

O algoritmo é o seguinte (depois de discussão entre os editores para ver o que era publicável):

  • passo 1: pegue um revolver revólver de borracha com seis tiros. Remova todos os cartuchos.
  • passo 2: coloque uma munição bala de borracha no tambor.
  • passo 3: utilizando a função random() % 4 selecione o presidente de uma operadora. Se sair 0 você pega o da Claro, se for 1 o da Oi, se for 2 o da Tim e se for 3 o da Vivo.
  • passo 4: gire o tambor do revólver arma de borracha aleatóriamente.
  • passo 5: Coloque um chip da operadora sorteada no seu celular.
  • passo 6: Coloque o cano do revólver da arma de borracha dentro da boca apontada para uma região “delicada” do presidente da operadora sorteada.
  • passo 7: Digite www.google.com no browser do celular.
  • passo 8: Se a página abrir em menos de 30s pule para o passo 11
  • passo 9: Puxe o gatilho.
  • passo 10: Se atirou arrume outro presidente dê um gelo para o presidente da para a operadora
  • passo 11: Remova a munição ou apenas a capsula do tambor.
  • passo 12: Retorne ao passo 2

Execute o algoritmo num processo colocado no crontab a cada hora. Em poucos dias teremos a solução do problema do 3G no Brasil ou pelo menos do desemprego …

Caso queira acelerar o processo use a roleta portuguesa (5 balas balas de borracha e duas apertadas no gatilho) no núcleo do algoritmo.

Este algoritmo é baseado no algoritmo chinês para resolver o bug do milênio em aeronaves.

Desenvolver aplicativos para Android requer uma certa dose de loucura

Off topic: Antes de mais nada, gostaria de me desculpar pela falta de atualização do blog no último mês. Este foi o segundo mês com menos posts de toda a história do blog (O primeiro foi o fatídico mês de janeiro de 2010 quando o NokiaBR foi fechado e tive que me organizar para continuar blogando). Andamos todos bastante ocupados com nossos trabalhos que não conseguimos escrever o quanto gostaríamos. Também acho que o Mobile Analyst anda comendo tanto ovo cozido na Cinelândia que se esqueceu de nós…

Mas vamos ao que interessa. Android. Plataforma aberta. Milhares de opções de aparelhos. Isso é bom para o usuário (tenho minhas dúvidas). Mas será que é bom para o desenvolvedor?

Venho desenvolvendo há alguns anos aplicativos para iOS e, no ano passado aqui na empresa começamos a portar alguns dos nossos aplicativos e jogos para Android. Para nossa surpresa, desenvolver um aplicativo no Android que funcione em todos os aparelhos requer muita paciência porque várias coisas que funcionam para uma versão do sistema não funcionam em outras versões. Às vezes, coisas que funcionam no 2.3 não existem no 2.2 e coisas do 2.3 não funcionam da mesma maneira no 3.0, e assim por diante. É preciso testar o programa no máximo de aparelhos que você consiga, o que é inviável.

Para completar a guerra, existe um número absurdo de tipos de tela (dimensões x densidade de pixels x orientação). Telas quadradas, telas verticais, telas horizontais, resolução baixa, resolução média, resolução alta, resolução extra alta, cada uma dessas com suas peculiaridades.

A cereja desse bolo é a chamada “customização” dos fabricantes e operadoras, que pode deixar a guerra ainda mais complicada para o desenvolvedor.

Os defensores do Android dizem que essa fragmentação é uma qualidade e não um problema. Garanto que quem diz isso nunca desenvolveu nada para Android ou nunca teve que responder a um cliente que vem reclamar que o seu aplicativo não roda no aparelho dele que tem tela hexagonal e nem no do amigo dele que tem tela elíptica em landscape. 🙂

Outro dia li num post do TechCrunch que um desenvolvedor chamado Animoca, chega a testar os seus aplicativos em 400 aparelhos diferentes (parte deles na foto abaixo).

E outro, esse eu li no Ars Technica, que diz já ter identificado 4.000, QUATRO MIL, modelos de aparelhos diferentes rodando um aplicativo dele (gráfico abaixo). Outra curiosidade que esse desenvolvedor revelou, é que nesses 4.000 modelos de aparelhos, ele descobriu quase 600 fabricantes diferentes! 599 para ser mais preciso.

Com os nossos aplicativos acontece exatamente a mesma coisa. Temos o exemplo do jogo “Manobrista Maluco“, que é sucesso no iOS e também no Android, que tem mais de 850.000 downloads no iOS e pouco mais 145.000 downloads no Android (a versão Android tem quase dois anos a menos).

O gráfico de aparelhos que usam esse jogo é o seguinte:

Como podemos reparar, somente 46% dos downloads estão concentrados em 9 aparelhos. Os outros 54% estão distribuídos em aparelhos que individualmente participam com menos de 2,6% do total. Se quiséssemos atender à maioria dos usuários, teríamos que testá-lo em centenas de aparelhos diferentes.

Hoje recebemos da Amazon um e-mail informando que o Manobrista Maluco não foi aceito para ser publicado na loja de aplicativos do Kindle Fire. Segundo eles, o jogo não se adapta às dimensões de tela do Kindle Fire. Teremos que adaptar o jogo para o Kindle se quisermos publicá-lo na loja da Amazon.

Cada vez mais vemos aparelhos com Android sendo vendidos nas mais variadas versões do sistema e com as mais diversas disposições de tela. Outro dia alguém me mandou uma foto de um flagra de uma loja de celulares vendendo aparelhos com Android com a versão 1.6 ainda! Isso sem falar dos Xing-Lings dos camelódromos que de MP10 viraram num passe de mágica aparelhos com Android.

Enfim, gostaria de saber se outros desenvolvedores de aplicativos para Android compartilham dessa opinião e o que eles fazem para contornar esse problema, além de testar os seus programas em N+K aparelhos.

Nokia pode se juntar a RIM

Nokia pode se juntar a RIM no grupo de empresas agonizantes.

O grupo das empresas agonizantes tem mais um membro: Nokia. É uma pena, mas é a lei do mercado, quem não se moderniza morre.

Com 10000 demissões estes dias, assitindo impotente os concorrentes crescerem e sem a mesma capacidade de inovação de antigamente a Nokia viu suas ações despencarem nesta semana quando a Microsoft anunciou que não ia comprar a fabricante Finlandesa. Em 3 meses as ações perderam mais de 50% de seu valor.

Apesar dos louváveis esforços de Stephen Elop, seus antecessores fizeram lambança demais e o barco vai afundando.

No entanto Tio Bill pode estar jogando a corda de salvação nesta segunda-feira quando provavelmente a Microsoft irá lançar um tablet com Windows 8, que provavelmente será fabricado pela Nokia.

Aguarde as cenas do próximo capítulo.

Ahhhh ia esquecendo: e a RIM? Esta morreu e ainda não contaram para eles.

Nós calculamos o valor real do novo MacBook Pro Retina para você !

Vendo todo o alvoroço sobre os preços do MacBook Pro Retina, resolvi fazer algumas contas para verificar se o preço estava realmente com ágio ou não. Para isso, dê uma olhada na tabela a seguir, onde os valores de todos os modelos de MacBook são apresentados, em dólares e reais, usando os preços encontrados no site da Apple para cada país.

Modelo US$ R$
MacBook Air 11 64GB 999 3699
MacBook Air 11 128GB 1099 3999
MacBook Air 13 128GB 1199 4999
MacBook Air 13 256GB 1499 6099
MacBook Pro 13 2.5GHz 1199 3999
MacBook Pro 13 2.9GHz 1499 5999
MacBook Pro 15 2.3GHz 1799 7999
MacBook Pro 15 2.6GHz 2199 9599
MacBook Pro 15 Retina 2.3GHz 2199 9999
MacBook Pro 15 Retina 2.6GHz 2799 12599

 

Para gerar uma comparação justa, criei o índice “AIR11”, que normaliza os valores dos MacBook em relação ao modelo de entrada, no caso o MacBook Air de 11 polegadas e 64GB. A ideia é ver quantos MacBooks Air 11 um outro MacBook qualquer vale. A tabela a seguir mostra este índice para os EUA e Brasil. É possível ver que o MacBook topo de linha vale 2,8 Air 11 nos EUA e 3,41 Air 11 no Brasil, algo que já indica uma suspeita de preços acima da média.

Modelo AIR11 Index (US) AIR11 Index (BR)
MacBook Air 11 64GB 1,00 1,00
MacBook Air 11 128GB 1,10 1,08
MacBook Air 13 128GB 1,20 1,35
MacBook Air 13 256GB 1,50 1,65
MacBook Pro 13 2.5GHz 1,20 1,08
MacBook Pro 13 2.9GHz 1,50 1,62
MacBook Pro 15 2.3GHz 1,80 2,16
MacBook Pro 15 2.6GHz 2,20 2,60
MacBook Pro 15 Retina 2.3GHz 2,20 2,70
MacBook Pro 15 Retina 2.6GHz 2,80 3,41

 

A comparação é melhor feita através do gráfico abaixo. Veja que a boa opção de compra é o MacBook Pro de 13 polegadas e 2.5GHz. Perceba também que os equipamentos mais caros apresentam uma distorção maior de preço. Não sei se o “custo Brasil” aumenta com o valor do equipamento. Se isto for verdade, poderia explicar a discrepância.

Custo dos MacBook em relação ao MacBook Air 11 64GB

No entanto, se não for esta a explicação, fica fácil calcular o ágio presente no produto. Neste caso, vamos aplicar o índice AIR11 americano nos valores brasileiro, para descobrir qual seria o preço Brasil, assumindo que a relação de preço americana seja a ideal. Isto é feito multiplicando-se o valor do MacBook Air 11 no Brasil pelo índices AIR11 americanos, como pode ser visto na tabela abaixo.

Modelo AIR11 Index (US) Preço Corrigido
MacBook Air 11 64GB 1,00 3.699,00
MacBook Air 11 128GB 1,10 4.069,27
MacBook Air 13 128GB 1,20 4.439,54
MacBook Air 13 256GB 1,50 5.550,35
MacBook Pro 13 2.5GHz 1,20 4.439,54
MacBook Pro 13 2.9GHz 1,50 5.550,35
MacBook Pro 15 2.3GHz 1,80 6.661,16
MacBook Pro 15 2.6GHz 2,20 8.142,24
MacBook Pro 15 Retina 2.3GHz 2,20 8.142,24
MacBook Pro 15 Retina 2.6GHz 2,80 10.363,86

 

Conclusão: aproximadamente 18% a mais nos MacBooks Retina e 16% nos macBook Pro de 15 polegadas.

Vale lembrar que não estou discutindo a margem de lucro, câmbio, etc, praticada pela Apple Brasil. Estou apenas assumindo que isto deveria ser similar ao americano e usando o valor de entrada do MacBook no Brasil. Se você quiser entrar nesta análise, deixo a tabela abaixo que mostra a relação entre Dólar e Real, para os mesmo equipamentos. Dá pra ver que o dólar tá valendo 4,5 reais para o Pro Retina.

Modelo US$ R$ Relação R$/US$
MacBook Air 11 64GB 999 3699 3,70
MacBook Air 11 128GB 1099 3999 3,64
MacBook Air 13 128GB 1199 4999 4,17
MacBook Air 13 256GB 1499 6099 4,07
MacBook Pro 13 2.5GHz 1199 3999 3,34
MacBook Pro 13 2.9GHz 1499 5999 4,00
MacBook Pro 15 2.3GHz 1799 7999 4,45
MacBook Pro 15 2.6GHz 2199 9599 4,37
MacBook Pro 15 Retina 2.3GHz 2199 9999 4,55
MacBook Pro 15 Retina 2.6GHz 2799 12599 4,50

 

Bacana, nhein, Apple Brasil ?

4G no Brasil, meu avô e o código morse: samba do “afro-brasileiro” classificado no DSM-IV-TR

O que tem a ver meu avô materno, com o código morse e com o 4G no Brasil? E porque você colocou no título samba do “afro-brasileiro” classificado no DSM-IV-TR?

Vamos por partes, como diria Jack o Estripador, eu queria dizer “samba do cri**lo doi*o” mas não posso fazê-lo sem ser acusado de racismo, ou políticamente incorreto. Assim substituí o termo crio**o por afro-brasileiro e doid* por pessoa classificada no DSM-IV-TR. São as agruras deste mundo políticamente correto.

O fato é que volto ao frango, quer dizer ao 4G. Nestes últimos dias vi que a Anatel (ó macabra piada) leiloou as frequências para operação 4G no Brasil e comecei a me perguntar: se o 3G não funciona aqui, o que leva estes caras a pensar que o 4G vai fazer algo de útil? E a situação é mais triste quando se fala na imprensa que o 4G permite velocidades até 10 vezes maiores que o 3G.

O fato é que o 3,5G que deveríamos ter aqui no Brasil permite velocidades de até 7,2Mbps. As operadoras que sugam o nosso suado dinheiro com a ajuda da incomPTencia do governo oferecem no máximo 3Mbps e olhe lá. O normal é oferecer 1Mbps, que você deve elevar as mãos aos céus quando funciona.

Desta forma, se o 4G ampliar a velocidade “real” que temos em 10 vezes, vamos ter muito menos que a Verizon entrega no 3G nas cidades americanas. Além disso, situações de 90% de perda de pacote, como mostramos antes, será que vão parar de acontecer?

E o que seu avô e o código morse tem a ver com isto? Chego lá. Meu avo, qepd, falecido há poucos anos, trabalhou durante toda sua vida como telegrafista. Segundo ele, mantinha um ritmo de 40 palavras por minuto em morse no seu telégrafo. Quando comecei a escrever este post, tentei enviar pelo 3G da Vivo e não consegui. Aí lembrei dele, se fosse em código morse o texto já teria sido publicado de manhã.

Fica uma homenagem aos telegrafistas. A banda era pequena mas não falhavam. Funcionava melhor que o 3G brasileiro.

Reverse engineering SWF to fix limitation in SWFTools

This post will be in English as you can see. And it’s very specific, maybe it won’t interest most of the usual readers.

A couple days ago I was hitting my head against an almost impossible to solve problem as I described in my cry for help at StackOverflow: swftools and events creating internal link

The solution was far from easy because debug in Flash is a great way to pay for your sins to save purgatory time. So I decided to take the way of reverse engineering.

Getting an working example and using swfdump -D in it I figured out that the swf dump should look like it:

                [022]       227 DEFINEBUTTON2 defines id 0011
                 condition 00a1
                 (   18 bytes) action: Push int:2 int:1 String:"butMC4"
                 (    0 bytes) action: GetVariable
                 (   13 bytes) action: Push String:"gotoAndPlay"
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    7 bytes) action: Push String:"_root"
                 (    0 bytes) action: GetVariable
                 (   11 bytes) action: Push String:"canflip" bool:false
                 (    0 bytes) action: SetMember
                 (    0 bytes) action: End
                 condition 0152
                 (   18 bytes) action: Push int:11 int:1 String:"butMC4"
                 (    0 bytes) action: GetVariable
                 (   13 bytes) action: Push String:"gotoAndPlay"
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    7 bytes) action: Push String:"_root"
                 (    0 bytes) action: GetVariable
                 (   11 bytes) action: Push String:"canflip" bool:true
                 (    0 bytes) action: SetMember
                 (    0 bytes) action: End
                 condition 0008
                 (   25 bytes) action: Constantpool(3 entries) String:"_root" String:"canflip" String:"gotoPage"
                 (    2 bytes) action: Push Lookup:0 ("_root")
                 (    0 bytes) action: GetVariable
                 (    4 bytes) action: Push Lookup:1 ("canflip") bool:true
                 (    0 bytes) action: SetMember
                 (   14 bytes) action: Push bool:true int:10 int:2 Lookup:0 ("_root")
                 (    0 bytes) action: GetVariable
                 (    2 bytes) action: Push Lookup:2 ("gotoPage")
                 (    0 bytes) action: CallMethod
                 (    0 bytes) action: Pop
                 (    0 bytes) action: End

To have the events implemented.

I changed the swftools, to get the pdf2swf producing the correct code to the internal links. It was an iterative process. Change C code, compile, run, compare against correct dump.

The final form of the function

void swfoutput_linktopage(gfxdevice_t*dev, int page, gfxline_t*points)
{
    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
    ActionTAG* actions = 0;
    ActionTAG* actionOvOn = 0;
    ActionTAG* actionOvOff = 0;
 
    if(i->shapeid>=0)
	endshape(dev);
    if(i->textmode)
	endtext(dev);
        /* 
            _root.canflip=true; // flipping enabled
            _root.gotoPage(4,true); // go to page
        */
	actions = action_PushString(actions, "_root"); //function name
        actions = action_GetVariable(actions);
	actions = action_PushString(actions, "canFlip"); //function name
	actions = action_PushBoolean(actions,1);
        actions = action_SetMember(actions);
	actions = action_PushBoolean(actions,0);
	actions = action_PushInt(actions, page); //parameter
	actions = action_PushInt(actions, 2); //number of parameters (2)
	actions = action_PushString(actions, "_root"); //function name
        actions = action_GetVariable(actions);
        actions = action_PushString(actions,"gotoPage");
	actions = action_CallMethod(actions);
        actions = action_Pop(actions);
	actions = action_End(actions);
 
        /* 
            _root.canflip=false; // flipping enabled
        */
        actionOvOn = action_PushString(actionOvOn, "_root"); //function name
        actionOvOn = action_GetVariable(actionOvOn);
        actionOvOn = action_PushString(actionOvOn, "canFlip"); //function name
        actionOvOn = action_PushBoolean(actionOvOn,0);
        actionOvOn = action_SetMember(actionOvOn);
	actionOvOn = action_End(actionOvOn);
 
        /* 
            _root.canflip=true; // flipping enabled
        */
        actionOvOff = action_PushString(actionOvOff, "_root"); //function name
        actionOvOff = action_GetVariable(actionOvOff);
        actionOvOff = action_PushString(actionOvOff, "canFlip"); //function name
        actionOvOff = action_PushBoolean(actionOvOff,1);
        actionOvOff = action_SetMember(actionOvOff);
	actionOvOff = action_End(actionOvOff);
 
        printf("Debug: Actions GotoPage %d\n",page);
        char name[80];
        sprintf(name, "page%d", page);
 
	drawlink3 (dev,actions,actionOvOff,actionOvOn,points,page,name);
        swf_ActionFree(actions);
	swf_ActionFree(actionOvOn);
	swf_ActionFree(actionOvOff);
}

I had to create a new function to attach 3 events to a button. The optimal solution should have a variable number of events, but I didn’t have time for that.

static void drawlink3(gfxdevice_t*dev, ActionTAG*actions1, 
                      ActionTAG*actions2, ActionTAG*actions3, 
                      gfxline_t*points, char*type, const char*url)
{
    swfoutput_internal*i = (swfoutput_internal*)dev->internal;
    RGBA rgb;
    SRECT r;
    int lsid=0;
    int fsid;
    int myshapeid;
    int myshapeid2;
    double posx = 0;
    double posy = 0;
    int buttonid = getNewID(dev);
    gfxbbox_t bbox = gfxline_getbbox(points);
 
    i->hasbuttons = 1;
 
    /* shape */
    myshapeid = getNewID(dev);
    i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
    swf_ShapeNew(&i->shape);
    rgb.r = rgb.b = rgb.a = rgb.g = 0; 
    fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
    swf_SetU16(i->tag, myshapeid);
    r.xmin = (int)(bbox.xmin*20);
    r.ymin = (int)(bbox.ymin*20);
    r.xmax = (int)(bbox.xmax*20);
    r.ymax = (int)(bbox.ymax*20);
    r = swf_ClipRect(i->pagebbox, r);
    swf_SetRect(i->tag,&r);
    swf_SetShapeStyles(i->tag,i->shape);
    swf_ShapeCountBits(i->shape,NULL,NULL);
    swf_SetShapeBits(i->tag,i->shape);
    swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
    i->swflastx = i->swflasty = 0;
    drawgfxline(dev, points, 1);
    swf_ShapeSetEnd(i->tag);
    swf_ShapeFree(i->shape);
 
    /* shape2 */
    myshapeid2 = getNewID(dev);
    i->tag = swf_InsertTag(i->tag,ST_DEFINESHAPE3);
    swf_ShapeNew(&i->shape);
 
    rgb = i->config_linkcolor;
 
    fsid = swf_ShapeAddSolidFillStyle(i->shape,&rgb);
    swf_SetU16(i->tag, myshapeid2);
    r.xmin = (int)(bbox.xmin*20);
    r.ymin = (int)(bbox.ymin*20);
    r.xmax = (int)(bbox.xmax*20);
    r.ymax = (int)(bbox.ymax*20);
    r = swf_ClipRect(i->pagebbox, r);
    swf_SetRect(i->tag,&r);
    swf_SetShapeStyles(i->tag,i->shape);
    swf_ShapeCountBits(i->shape,NULL,NULL);
    swf_SetShapeBits(i->tag,i->shape);
    swf_ShapeSetAll(i->tag,i->shape,/*x*/0,/*y*/0,0,fsid,0);
    i->swflastx = i->swflasty = 0;
    drawgfxline(dev, points, 1);
    swf_ShapeSetEnd(i->tag);
    swf_ShapeFree(i->shape);
 
    // ST_DEFINEBUTTON2 support multiple conditions
    // ST_DEFINEBUTTON Don't!!!
    i->tag = swf_InsertTag(i->tag,ST_DEFINEBUTTON2); 
    swf_SetU16(i->tag,buttonid); //id
    swf_ButtonSetFlags(i->tag, 0); //menu=no
    swf_ButtonSetRecord(i->tag,0x01,myshapeid,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x02,myshapeid2,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x04,myshapeid2,i->depth,0,0);
    swf_ButtonSetRecord(i->tag,0x08,myshapeid,i->depth,0,0);
    swf_SetU8(i->tag,0);
 
    // on(rollOver, dragOver)
    swf_ButtonSetCondition(i->tag,0x00a1);
    swf_ActionSet(i->tag,actions3);
 
    //on(rollOut, dragOut, releaseOutside)
    swf_ButtonSetCondition(i->tag,0x0152);
    swf_ActionSet(i->tag,actions2);
 
    // on(release)
    swf_ButtonSetCondition(i->tag,0x0008);
    swf_ActionSet(i->tag,actions1);
 
    swf_SetU8(i->tag,0);
    swf_ButtonPostProcess(i->tag,3);
 
    char buf[80];
    char*buf2 = 0;
    const char* name = 0;
    if(i->config_linknameurl) {
        buf2 = malloc(strlen(type)+strlen(url)+2);
        sprintf(buf2, "%s:%s", type, url);
        name = buf2;
    } else {
        name = buf;
        sprintf(buf, "button%d", buttonid);
    }
 
    i->tag = swf_InsertTag(i->tag,ST_PLACEOBJECT2);
 
    if(posx!=0 || posy!=0) {
        SPOINT p;
        p.x = (int)(posx*20);
        p.y = (int)(posy*20);
        p = swf_TurnPoint(p, &i->page_matrix);
	MATRIX m;
        m = i->page_matrix;
        m.tx = p.x;
        m.ty = p.y;
	swf_ObjectPlace(i->tag, buttonid, 
                  getNewDepth(dev),&m,0,(U8*)name);
    } else {
	swf_ObjectPlace(i->tag, buttonid, 
                  getNewDepth(dev),&i->page_matrix,0,(U8*)name);
    }
 
    if(buf2)
	free(buf2);
}

Deu trabalho …