Mobile

24 set, 2018

Geolocalização 360

Publicidade

O uso de geolocalização em aplicativo mobile é um dos pontos fortes desse tipo de plataforma. A sensibilidade ao contexto e à localização pode dar um poder incomparável às soluções de software para Android, iOS e similares.

Historicamente, o uso do GPS (Global Position System) é a forma de georeferenciamento mais conhecida e mais utilizada pelos desenvolvedores.

Diferentemente de alguns anos atrás, quando a obtenção e parser dos dados era um processo penoso, atualmente existem SDKs e bibliotecas que tornam esse processo indolor e transparente para nós, programadores.

Porém, o uso de GPS tem algumas desvantagens, que em alguns momentos podem se sobrepor à ideia de um dado de posição muito próximo da realidade. Vou listar alguns deles abaixo:

  • Dificuldade de uso em ambientes indoor;
  • Dificuldade de visada dos satélites em ambientes de mata fechada ou quando o tempo está cheio de nuvens;
  • Dificuldade de visada dos satélites em túneis;
  • Necessidade do usuário ativar a localização nas configurações do aparelho;
  • Ao usar a biblioteca de geolocalização, o usuário da aplicação verá, de forma muito clara, que a aplicação está lendo sua posição latitude/longitude. Veja na figura o terceiro ícone da esquerda para a direita.

Mas talvez um dos pontos mais importantes sobre a não utilização do GPS em alguns momentos seja seu custo-benefício em relação a consumo de bateria e erro de posicionamento.

Como sabemos, esse método traz o menor erro médio de posição no globo terrestre. Porém, também é o responsável pelo maior consumo de bateria em relação aos outros métodos (Cell ID, TDOA, dentre outros).

Às vezes, o custo do consumo da bateria não vale a pena, conforme a necessidade de posicionamento. Por exemplo, imagine um sistema que coordena uma frota de caminhões de frete. Esses veículos viajam pelo país inteiro, e a necessidade dos gerentes é apenas saber, aproximadamente, onde estão – sendo que apenas a cidade e o estado são realmente necessários. Por que iríamos usar GPS e todo seu poder para algo tão simples?

Pensando dessa forma, avançamos na linha de raciocínio. Ok, se o GPS pode não ser o método perfeito em um dado momento, que opções teríamos? Para responder totalmente a essa pergunta, seriam necessárias bem mais páginas. Seria necessário entrar a fundo no assunto de geoposicionamento, latitude, longitude e afins. Mas acredito que não seja preciso ir tão longe.

Hoje, nós, desenvolvedores, temos acesso a uma biblioteca que nos dá suporte facilitado para o método de geoposicionamento conhecido como CellID. O Cellular IDentification é a identificação da célula onde seu aparelho está conectado.

Ou seja, se soubermos onde essa estação está, é muito provável que estejamos a uma distância razoavelmente perto, a qual podemos considerar e informar ao usuário.

A API a que me refiro é a Google Maps Geolocation API. Ela fornece uma API com chamadas através de requisições HTTP, semelhante a um serviço web REST – basta informar um JSON de envio com os dados das estações onde o dispositivo está conectado. O retorno também será um JSON.

Um exemplo de JSON de requisição seria este (envio via POST):

{
  "radioType": "gsm",
  "cellTowers": [
    {
      "cellId": 50108,
      "locationAreaCode": 946,
      "mobileCountryCode": 724,
      "mobileNetworkCode": 5,
      "signalStrength": -95
    }
 ]
}

E um exemplo de resposta do serviço seria:

{
    "location": {
        "lat": -26.214344600000004,
        "lng": -52.6781696
    },
    "accuracy": 2619
}

Para formatar o JSON de envio, é utilizada basicamente a classe TelephonyManager, do pacote android.telephony. Seu método getPhoneType() traz um valor inteiro, que pode ser comparado com algumas das constantes da classe: PHONE_TYPE_GSM e PHONE_TYPE_CDMA, por exemplo.

Para recuperar os dados das torres conectadas ao aparelho, existem dois métodos, cada um específico para uma versão do Android. Se o aparelho for uma versão menor que o Android Marshmallow, o método a ser chamado é o getNeighboringCellInfo(). Caso contrário, o método a ser chamado é o getAllCellInfo().

No primeiro caso, é retornada uma lista com instâncias de NeighboringCellInfo, enquanto que no segundo, uma lista de instâncias de CellInfo. Mas ambas têm métodos autoexplicativos que vêm ao encontro dos dados exigidos pela API do Google, como getCid e getLac no NeighboringCellInfo, e getCid, getMnc, getMcc e getLac no segundo caso.

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
   List<NeighboringCellInfo> neighCells = tel.getNeighboringCellInfo();
   for (int i = 0; i < neighCells.size(); i++) {
       try {
           JSONObject cellObj = new JSONObject();
           NeighboringCellInfo thisCell = neighCells.get(i);
           cellObj.put("cellId", thisCell.getCid());
           cellObj.put("lac", thisCell.getLac());
           cellObj.put("rssi", thisCell.getRssi());
           cellList.put(cellObj);
       } catch (Exception e) {
       }
   }

} else {
   List<CellInfo> infos = tel.getAllCellInfo();
   for (int i = 0; i<infos.size(); ++i) {
       try {
           JSONObject cellObj = new JSONObject();
           CellInfo info = infos.get(i);
           if (info instanceof CellInfoGsm){
               CellSignalStrengthGsm gsm = ((CellInfoGsm) info).getCellSignalStrength();
               CellIdentityGsm identityGsm = ((CellInfoGsm) info).getCellIdentity();
               cellObj.put("cellId", identityGsm.getCid());
               cellObj.put("locationAreaCode", identityGsm.getLac());
               cellObj.put("mobileCountryCode", identityGsm.getMcc());
               cellObj.put("mobileNetworkCode", identityGsm.getMnc());
               cellObj.put("signalStrength", gsm.getDbm());
               cellList.put(cellObj);
           }
       } catch (Exception ex) {}
   }
}

Depois que os dados estão inseridos na estrutura do JSON que a API exige, basta fazer uma requisição web com POST e ler o retorno. Qualquer biblioteca para HTTP é bem-vinda – pode ser Retrofit, Volley ou Ion.

E a Google Maps Geolocation API ainda fornece um bônus. É possível recuperar latitude e longitude enviando dados da rede Wi-Fi de onde nós estamos conectados. O JSON também é muito simples, sendo que o único dado obrigatório é o macAddress. Veja o exemplo abaixo:

{
  "considerIp": "false",
  "wifiAccessPoints": [
    {
        "macAddress": "00:25:9c:cf:1c:ac",
        "signalStrength": -43,
        "signalToNoiseRatio": 0
    },
    {
        "macAddress": "00:25:9c:cf:1c:ad",
        "signalStrength": -55,
        "signalToNoiseRatio": 0
    }
  ]
}

Em um teste realizado no prédio onde eu moro, a precisão da latitude e da longitude obtida com o Geolocation API e Wi-Fi foi maior que com os dados do GPS. Nada mal, né?

E o código é fácil? Sim, com certeza.

Existem duas formas. A primeira é onde eu capturo o macAddress da rede onde estou conectado (primeira listagem). A segunda serve para escanear as redes próximas e pegar os macAddress delas. Segundo a documentação da Geolocation API, quanto mais endereços de hotspots, mais precisa será a resposta.

WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
String macAddress = info.getMacAddress();


WifiManager manager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
manager.startScan();
List<ScanResult> results = manager.getScanResults();
for (ScanResult result : results)
{
   String macAddress = result.BSSID;
}

Espero que o leitor tenha gostado destas novas formas de geoposicionamento. O mundo LBS (Location Base System) é muito mais amplo e rico do que somente o GPS. Com a Geolocation API, pesquisando latitude e longitude por Cell ID e/ou Wi-Fi, o desenvolvedor ganha armas para criar aplicativos melhores e menos intrusivos.

Obrigado!

***

Artigo publicado na revista iMasters, edição #26: https://issuu.com/imasters/docs/imasters_26_v6_isuu