o flash é uma tarefa impossível, mas nada motivo para baixar os braços. outra aplicação que me interessou desenvolver consistia em procurar routers nas redondezas e, com base em vulnerabilidades conhecidas, ligar-se a eles. claro que os algoritmos sobre as vulnerabilidades não são meus, não tenho a destreza para esse trabalho, mas é uma boa maneira de iniciar interfaces. chamei-lhe Wi-Fi Smith. =)
desta vez custou um pouco mais começar, pois foi preciso ler um pouco sobre controlos e containers. no fundo não há muita diferença relativamente a outros toolkits gráficos. comparando com gtk, que foi o último que usei, para o bada também tenho duas hipóteses: posso programar e colocar os controlos todos no sítio com código, ou posso desenhar no ide e depois, no código, pedir para que sejam colocados no sítio e obter o handle. nada de especial.
o problema que há com este modo de programação do bada, para mim, é que as callbacks para os módulos geradores de eventos não são passadas explicitamente. o que é passado é uma classe que implemente (herde, em c++) uma interface IModuleEventListener. além disto não podemos definir argumentos para passar a essas funções. resultado: torna-se complicado gerir a partilha de recursos a serem utilizados na rotina de atendimento. o que é que eu fiz? o módulo principal herda tudo… felizmente não há colisões, mas neste caso não vi que fizesse sentido criar módulos diferentes. uma vez que vou tratar de eventos wifi, e da interface, bem como os genéricos das aplicações, isto é a classe criada:
class WifiSmith :
public Osp::App::Application,
public Osp::System::IScreenEventListener,
public Osp::Ui::IActionEventListener,
public Osp::Net::Wifi::IWifiManagerEventListener,
public Osp::Ui::IItemEventListener
{
em vez de dar uma caixa de texto e um botão “do the magic” achei que seria mais interessante (e até mais simples) limitar-me a pegar no wi-fi, obter uma lista das redes disponíveis e listá-las. isto foi muito fácil de fazer. ao construir o manifest.xml no site, tive de adicionar privilégio WIFI. isto permite fazer todas as operações com o módulo wi-fi, excepto Connect(). para ligar é necessário permissões WIFI_MANAGER. infelizemente, só parceiros da samsung podem pertencer a este grupo, pois é necessário um nível de acesso SYSTEM. uma parvoíce, a meu ver. qualquer que seja a parte da api, não vejo motivo para que alguém que pague (ou receba?) tenha acesso e os outros não. no entanto esta limitação apenas impede publicação na loja; o grupo pode ser adicionado manualmente no ficheiro xml, mas a aplicação será certamente rejeitada na loja:
<Privileges> <Privilege> <Name>WIFI</Name> </Privilege> <Privilege> <Name>WIFI_MANAGER</Name> </Privilege> </Privileges>
para compilar com permissões especiais é preciso, com frequência, adicionar uma biblioteca para linkagem, neste caso FNet. verdade para o simulador, para o telemóvel parece-me estar tudo na mesma biblioteca, FOsp.
a interface da aplicação é muito simples. dois botões em baixo, um “refresh” e outro “back”. gosto das aplicações que têm botão de saída no ecrã, não gosto de usar o físico. no editor visual: as duas SOFTKEY activas e uma List que ocupa todo o ecrã. a List permite scroll se os elementos excederem o espaço reservado. a SlidableList serve para listas grandes, e alocação dinâmica dos elementos que interessam ver num dado momento apenas. no início desenho esta estrutura, associo as callbacks, e inicializo as variáveis necessárias:
bool
WifiSmith::OnAppInitializing(AppRegistry& appRegistry)
{
// draw the form
MainForm *pMainForm = new MainForm();
pMainForm->Construct(L"IDF_FORM1");
Frame *pFrame = GetAppFrame()->GetFrame();
pFrame->AddControl(*pMainForm);
pFrame->SetCurrentForm(*pMainForm);
pMainForm->SetSoftkeyActionId(SOFTKEY_1, SOFTKEY_1);
pMainForm->AddSoftkeyActionListener(SOFTKEY_1, *this); // register back button event
pMainForm->SetSoftkeyActionId(SOFTKEY_0, SOFTKEY_0);
pMainForm->AddSoftkeyActionListener(SOFTKEY_0, *this); // register refresh button event
pMainForm->RequestRedraw();
// set the wifimanager
pWifiManager.Construct(*this);
// get the essid list control
pEssidListControl = (List*)pMainForm->GetControl("IDC_LIST1");
pEssidListControl->AddItemEventListener(*this);
return true;
}
que no fim liberto:
bool
WifiSmith::OnAppTerminating(AppRegistry& appRegistry, bool forcedTermination)
{
// decided to use dynamic allocation for the WifiManager and the list of WifiBssInfo (networks)
delete pEssidListControl;
return true;
}
sair da aplicação faz-se chamando o método Application::Terminate(). este método parece só estar acessível no módulo principal. tentei mesmo Application::GetInstance()->Terminate() noutros módulos, mas nem assim consegui compilar. azelhice minha, talvez. a cada refresh limpa-se e volta-se a preencher a lista de redes disponíveis:
void
WifiSmith::OnWifiActivated(result r)
{
pWifiManager.Scan();
}
// SOFTKEYs
void
WifiSmith::OnActionPerformed(const Osp::Ui::Control &source, int actionId)
{
// SOFTKEY_0 is just the actionId I associated with it on line 42, it's not really the key.
if(actionId == SOFTKEY_0) // refresh
{
RefreshList();
}
else
{
Application::Terminate();
}
}
void
WifiSmith::RefreshList()
{
// clear the essid list
pEssidListControl->RemoveAllItems();
pEssidListControl->RequestRedraw();
// turn on wifi, if off
if(!pWifiManager.IsActivated())
{
MessageBox vMessageBox;
vMessageBox.Construct("Action required!", "WiFi needs to be ON for scanning! Turn on?", MSGBOX_STYLE_YESNO, 0);
int modalResult;
vMessageBox.ShowAndWait(modalResult);
if(modalResult == MSGBOX_RESULT_YES)
{
pWifiManager.Activate();
}
else
{
Application::Terminate();
}
}
else
{
// get the available networks
OnWifiActivated(E_SUCCESS);
}
}
quando o wi-fi está desligado é na função refresh que pergunto ao utilizador se pretende activar, com uma MessageBox. se não o fizer, a aplicação termina. faria sentido chamar a RefreshList() na inicialização. no entanto há um problema de concorrência. enquanto a aplicação está a arrancar é mostrada uma “imagem splash”. por algum motivo a aplicação bloqueia nesse splash se tentar abrir uma MessageBox algures na função OnAppInitializing(). provavelmente funcionaria na função OnForeground(), mas nesse caso tinha de controlar o estado da aplicação com uma nova variável, pois a função OnForeground() é chamada sempre que se volta ao foreground, e não apenas da primeira vez. continuo a achar que o problema é do sistema operativo, algo diferente devia acontecer.
findo o Scan(), preenche-se novamente a lista:
void
WifiSmith::OnWifiScanCompletedN(const Osp::Base::Collection::IList *pWifiBssInfoList, result r)
{
pNetworkList.RemoveAll();
pNetworkList.AddItems(*pWifiBssInfoList);
for(int r = 0; r < pNetworkList.GetCount(); r++)
{
WifiBssInfo *currentBssInfo = (WifiBssInfo*)pNetworkList.GetAt(r);
pEssidListControl->AddItem(¤tBssInfo->GetSsid(), null, null, null, r);
}
pEssidListControl->RequestRedraw(true);
}
para quem não viu até aqui, é fácil perceber que não há muito tratamento de erros. esta função, por exemplo, indica com a variável r se o Scan() teve ou não sucesso. no entanto pouca diferença faz para o código que escrevi. mais, por motivos de eficiência, o mecanismo de controlo de excepções do c++ foi completamente ignorado pela samsung, não existem excepções no bada. o compilador até me avisou que, na linha 111, estou a passar um endereço de uma variável temporária. como nunca me deu problemas, não senti necessidade de copiar os nomes para uma lista.
depois de listadas as redes, é possível ver a listagem no telemóvel. clicando em qualquer rede, tenta-se ligar:
void
WifiSmith::OnItemStateChanged(const Osp::Ui::Control &source, int index, int itemId, Osp::Ui::ItemStatus status)
{
WifiBssInfo *pWifiBssInfo = (WifiBssInfo*)pNetworkList.GetAt(itemId);
const WifiSecurityInfo *pWifiSecurityInfo = pWifiBssInfo->GetSecurityInfo();
// if open and no encryption, go right ahead!
if(pWifiSecurityInfo->GetAuthenticationType() == WIFI_AUTHENTICATION_OPEN
&& pWifiSecurityInfo->GetEncryptionType() == WIFI_ENCRYPTION_NONE)
{
pWifiManager.Connect(*pWifiBssInfo);
}
}
lembre-se que, na linha 111, dei um itemId a cada elemento da List igual ao índice dessa rede na lista de redes. no fim da tentativa de ligação à rede, posso informar o utilizador:
void
WifiSmith::OnWifiConnected(const Osp::Base::String &ssid, result r)
{
MessageBox vMessageBox;
int modalResult;
if(r == E_SUCCESS)
{
vMessageBox.Construct("Connected:", ssid, MSGBOX_STYLE_OK, 0);
}
else
{
vMessageBox.Construct("Unable to connect:", ssid, MSGBOX_STYLE_OK, 0);
}
vMessageBox.ShowAndWait(modalResult);
}
por vários motivos, neste código só me ligo a redes abertas. o primeiro é o facto de ainda não ter tratado disso. o segundo é que, quando fui a testar o caso dos routers thomson, por exemplo, acabei por descobrir que a resolução das chaves é um tanto ou quanto probabilística, e nem sequer funcionou para alguns casos que testei. o terceiro motivo é o facto de subscrever um aditivo internet, como faz todo o sentido, com este telemóvel, e até porque não frequento sítios atulhados de redes vulneráveis.
no entanto acrescentar esta funcionalidade, para mim, faz-se do seguinte modo: começa-se por criar uma classe que recebe um WifiBssInfo e, com essa informação (provavelmente só ssi e bssid), identifica o tipo de router com que está a lidar e devolve, de acordo, a chave a utilizar. pode ainda disponibilizar um método booleano do tipo CanSolve() o que permitirá, no tempo da listagem, mostrar apenas as redes com que é possível lidar. por último, uma vez que alguns algoritmos devolvem mais que uma chave, a função OnWifiConnected() deve implementar uma máquina de estados que permita tentar a chave seguinte para a mesma rede, caso a ligação tenha falhado, e até que não existam mais chaves. nada mais!
algumas capturas:
(uau, this sucks! é preciso mesmo clicar nas imagens, parece que os thumbnails do wordpress cortam a imagem…)



