Google Maps Geolocation With CellID and WiFi

Page 1

GEOLOCATION ALÉM DO GPS

GOOGLE MAPS GEOLOCATION COM CELLID E WIFI

Introdução Um número muito grande de aplicativos fazem uso de alguma forma de geolocalização dos usuários. Os dados oriundos são geralmente do GPS ou A-GPS, acrônimos para Global Position System e Assisted Global Position System. O hardware necessário para receber estas informações está presente na quase totalidade dos aparelhos. Já é uma feature ​ muito comum nos smartphones. Como ponto positivo desta forma de geolocalização temos a precisão muito grande. É a melhor comparada a outros métodos. O sistema de posicionamento por satélites é tão


eficaz que foi “copiado” pelo sistema europeu Galileo, o russo Glonass e o chinês Compass. Porém, existem alguns pontos negativos. O GPS consome muita bateria do aparelho e, em algumas situações o aplicativo não necessita saber exatamente em qual rua a pessoa está, basta saber a cidade e o bairro. O nível de exatidão ao nível de poucas dezenas de metros do GPS é dispensável neste caso. Além disso, o GPS tem alguns pontos de sombra, como túneis, mata extremamente densa, ambiente indoor, além de ter sua qualidade afetado quando o tempo está ruim, como em tempestades e chuvas torrenciais. Não estamos dizendo que o GPS/A-GPS é ruim, apenas que existem alternativas e, para alguns conceitos de aplicativos, até melhores que a famosa triangulação por antenas. Neste artigo vamos tratar de duas formas muito usuais. Uma delas é através do CellID (identificação de uma célula gerada por uma estação rádio base) e a outra é via o access point onde o smarpthone está conectado para receber sinal de uma rede Wi-Fi. Em ambos os casos será usado a API do Google Maps Geolocation.

O que é Cell ID? Sempre que seu smartphone está recebendo e fazendo ligações e/ou lhe permite navegar na internet, isso significa que seu aparelho está na área de cobertura de uma estação rádio base da sua operadora. Cada antena tem um raio de alcance. Com o conjunto de diversas antenas espalhadas pelas cidades temos uma topologia semelhante a células, por isso o nome telefone celular, avô do smartphone. Toda célula criada por uma antena possui um identificador único e a mesma está fisicamente fixada em um lugar na terra. Esta posição é definida pela sua latitude e longitude.

A aplicação proposta A aplicação proposta será composta de apenas duas telas. A primeira mostrará as informações da cell id na qual o smartphone está ligado e, caso o aparelho também

2


esteja em uma rede Wi-Fi mostrará o SSID e o Mac Address da mesma. Veja na Figura 1 a tela inicial:

Figura 1: Tela inicial da aplicação

Ao clicar nas opções de “​ Buscar Posição​ ” a latitude e longitude vai aparecer em uma

TextView ​ localizada embaixo do texto “​ Resultado​ ”. Depois que os dados de geolocalização foram buscados é possível clicar no botão ​ “Ver no Mapa”​ . Na figura 2 é ilustrado o

momento em que o usuário busca a posição e o resultado é mostrado no local citado anteriormente:

3


Figura 2: Informação de latitude e longitude sendo exibida ao usuário.

Depois de buscar a posição o usuário pode clicar no botão “​ Ver no Mapa​ ”. Neste caso, a

aplicação abre a tela mostrada na Figura 3.

4


Figura 3: Visualização no mapa da informaçõa de geoposicionamento.

OBSERVAÇÃO: o projeto está compartilhado no github no seguinte endereço: https://github.com/ricardoogliari/GeolocationAPICellID.

Construção dos layouts Este texto é direcionado a programadores com, no mínimo, uma pequena experiência na plataforma Android. Sendo assim, não vou me deter na explicação detalhada da construção dos layout. O​ activity_main ​ representa o layout da tela principal. Veja a Listagem 1:

5


Listagem 1: activity_main.xml <​ LinearLayout ​ xmlns:​ android​ =​ "http://schemas.android.com/apk/res/android" ​ xmlns:​ tools​ =​ "http://schemas.android.com/tools" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "match_parent" android​ :paddingLeft=​ "@dimen/activity_horizontal_margin" ​ android​ :paddingRight=​ "@dimen/activity_horizontal_margin" ​ android​ :paddingTop=​ "@dimen/activity_vertical_margin" ​ android​ :orientation=​ "vertical" ​ android​ :paddingBottom=​ "@dimen/activity_vertical_margin" tools​ :context=​ ".MainActivity"​ > <​ TextView ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "About CellID" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMnc" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMcc" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtLac" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtCid" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "getPositionByCellId" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Buscar Posiçao"​ />

6


<​ View ​ android​ :layout_marginTop=​ "20dp" ​ android​ :layout_marginBottom=​ "15dp" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "1dp" ​ android​ :background=​ "#000000"​ /> <​ TextView ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "About Wi­Fi" ​ android​ :id=​ "@+id/textView2" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtSSID" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMacAddress" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "getPositionByWiFi" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Buscar Posiçao"​ /> <​ View ​ android​ :layout_marginTop=​ "20dp" ​ android​ :layout_marginBottom=​ "15dp" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "1dp" ​ android​ :background=​ "#000000"​ /> <​ TextView ​ android​ :textStyle=​ "bold" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "Resultado" ​ />

7


<​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtLatLng" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "seeInMaps" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Ver no mapa"​ /> </​ LinearLayout​ >

A tela do mapa é exatamente igual aquela gerada automaticamente pelo Android Studio quando uma ​ Google Map Activity ​ é criada. Veja a Listagem 2:

Listagem 2: activity_maps.xml <​ fragment ​ xmlns:​ android​ =​ "http://schemas.android.com/apk/res/android"

xmlns:​ tools​ =​ "http://schemas.android.com/tools"

android​ :layout_width=​ "match_parent"

android​ :layout_height=​ "match_parent" ​ android​ :id=​ "@+id/map"

android​ :name=​ "com.google.android.gms.maps.SupportMapFragment" ​ />

Recuperando informações de CellID e WiFi Antes de recuperar as informações é necessário configurar algumas permissões de usuário no arquivo ​ AndroidManifest.xml, ​ como segue abaixo:

<​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_COARSE_LOCATION" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.INTERNET" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_NETWORK_STATE" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_WIFI_STATE" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.WRITE_EXTERNAL_STORAGE" ​ />

8


<​ uses­permission android​ :name=​ "com.google.android.providers.gsf.permission.READ_GSERVICES" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_FINE_LOCATION" ​ />

O código da listagem 3 está inserido dentro do método ​ onCreate, ​ que faz parte do ciclo de vida da ​ Activity. ​ Relembrando que qualquer dúvida o leitor tem acesso ao código completo no GitHub. O endereço foi passado no final do capítulo que introduz a aplicação que será construída. Para acessar dados de geoposicionamento através de CellID é usada a classe TelephonyManager, ​ que pode ser instanciada através do método ​ getSystemService. ​ Como pode ser visto, este é um serviço do próprio sistema operacional Android.

No teste lógico ​ if ​ que está sendo feito no código, nos certificamos que estamos trabalhando com um dispositivo que está em uma rede GSM. Desta forma, as informações desejadas são: ● LAC: location area code; ● CID: Cell ID; ● MCC: mobile country code. Este código é único para cada país. Para o Brasil o MCC

é 724. Para uma lista completa acesse este link: https://en.wikipedia.org/wiki/Mobile_country_code​ .

● MNC: mobile network code. um código específico para cada operadora. Para uma

listagem completa acesse este link: http://www.mcc-mnc.com/. Na linha 3 é chamado o método ​ getCellLocation ​ e passado seu retorno para a variável da classe ​ GsmCellLocation. ​ Com esta variável em mãos é possível fazer chamadas aos

métodos ​ getLac(), getCid() ​ e​ getNetworkOperator(). ​ Uma observação é importante no

último método citado. Os três primeiros números referem-se ao MCC e, os três últimos ao MNC. Na linha 16 começa a lógica para buscar os dados da rede WiFi, caso o smartphone esteja conectado a uma. Inicialmente é recuperada a instância de ​ WifiManager, ​ que,

9


também é um serviço do sistema operacional. Com esta classe em mãos o método getConnectionInfo ​ é acionado, trazendo como resposta uma instância da classe ​ WifiInfo. Se o ​ WifiInfo ​ não estiver nulo, significa que o aparelho está conectado em uma rede WiFi e podemos recuperar seu SSID (na linha 21) e seu ​ Mac Address ​ (linha 22). Listagem 3: código para recuperar informações de CellID e WiFi … 1:final ​ TelephonyManager t = (TelephonyManager)getSystemService(Context.​ TELEPHONY_SERVICE​ )​ ; 2:if ​ (t.getPhoneType() == TelephonyManager.​ PHONE_TYPE_GSM​ ) { 3: ​ final ​ GsmCellLocation location = (GsmCellLocation) t.getCellLocation()​ ; 4: ​ if ​ (location != ​ null​ ) { 5: ​ lac ​ = location.getLac()​ ; 6: ​ cid ​ = location.getCid()​ ; 7: 8: ​ txtLac​ .setText(​ "Lac: " ​ + ​ lac​ )​ ; 9: ​ txtCid​ .setText(​ "Cid: " ​ + ​ cid​ )​ ; 10: ​ networkOperator ​ = t.getNetworkOperator()​ ; 11: ​ txtMcc​ .setText(​ "MCC: " ​ + ​ networkOperator​ .substring(​ 0​ , ​ 3​ ))​ ;​ //MCCMNC 12: ​ txtMnc​ .setText(​ "MNC: " ​ + ​ networkOperator​ .substring(​ 3​ ))​ ;​ //MCCMNC 13: ​ } 14:} 15: 16:WifiManager mainWifi = (WifiManager) getSystemService(Context.​ WIFI_SERVICE​ )​ ; 17:WifiInfo currentWifi = mainWifi.getConnectionInfo()​ ; 18:if​ (currentWifi != ​ null​ ) 19:{ 20: ​ macAddress ​ = currentWifi.getMacAddress()​ ; 21: ​ txtSSID​ .setText(currentWifi.getSSID())​ ; 22: ​ txtMacAddress​ .setText(​ macAddress​ )​ ; 23:} ...

10


Georeferenciando o aparelho com a Google Maps Geolocation API Para uma leitura mais detalhada sobre a API indicamos a leitura do seguinte documento: The Google Maps Geolocation API. https://developers.google.com/maps/documentation/geolocation/intro​ . No uso da API é utilizado o formato JSON tanto no envio da requisição tanto quanto na resposta. Esse fato foi mais um motivo para utilizarmos o framework ​ Retrofit ​ para as

requisições HTTP. Sendo assim, foram criadas duas classes Java para espelhar a resposta da API do Google, que se assemelha com a Listagem 4:

Listagem 4: exemplo de retorno da API. { "location"​ ​ :{

"lat"​ ​ :​ 51.0​ , "lng"​ ​ : -​ 0.1

}, }

"accuracy"​ ​ :​ 1200.4

A Listagem 5 mostra as duas classes que espelham o JSON acima:

Listagem 5: classe Location. public class ​ Location { ​ double ​ lat​ ; ​ double ​ lng​ ; } public class ​ CellId { ​ float ​ accuracy​ ; ​ Location ​ location​ ; }

11


O framework ​ Retrofit ​ também exige a criação de uma interface que, através do uso de annotations ​ própria da biblioteca são indicados os ​ endpoints ​ que serão usados pelas requisições HTTP. Veja na Listagem 6 como ficou a nossa interface:

Listagem 6: interface CellIDService. public interface ​ CellIdService { ​ @POST​ (​ "/geolocation/v1/geolocate"​ ) ​ void ​ geolocate( @Body ​ String body​ , @Query​ (​ "key"​ ) String key​ , Callback<CellId> callback)​ ; }

Na classe ​ MainActivity ​ foram codificados os dois métodos que respondem ao clique nos botões da interface gráfica e, indicam que o usuário deseja saber a localização através

dos dados do Cell ID ou do WiFi. Inicialmente, vamos analisar o método ​ getPositionByWiFi. Veja a Listagem 7:

Logo na primeira linha é configurado o ​ endpoint ​ para o ​ RestAdapter, ​ da biblioteca do

Retrofit. ​ Ele é necessário para, na linha 3, criar a instância da ​ interface ​ que mostramos

anteriormente na Listagem 6. Com a variável ​ service ​ basta chamar o método ​ geolocate ​ e passar os parâmetros configurados na ​ interface ​ e, que são exigido pela API do Google Geolocation.

No primeiro parâmetro temos o ​ JSON ​ que envia o único parâmetro obrigatório, o

macAddress. ​ Para a leitura de todos os parâmetros que podemos passar já indicamos o site da documentação da API nos passos anteriores. O segundo parâmetro é a ​ api key, que pode ser criado no ​ Google Console API´s. ​ E, por fim, temos um ​ Callback ​ que nos

avisará quando a requisição foi feita e já houve uma resposta do servidor, seja ela de sucesso ou erro.

12


Na linha 7 é codificado o método de ​ callback ​ no caso de sucesso. Neste caso, basta

recuperar o dado de ​ latitude ​ e​ longitude ​ que já estão prontos na instância da classe ​ CellId. Agradeça ao ​ Retrofit ​ por isso.

Listagem 7: método getPositionByWiFi. public void ​ getPositionByWiFi(View view){ 1: RestAdapter retrofit = ​ new ​ RestAdapter.Builder() .setEndpoint(​ "https://www.googleapis.com"​ ) .build()​ ; 2: 3: ​ CellIdService service = retrofit.create(CellIdService.​ class​ )​ ; 4: ​ service.geolocate(​ "{​ \n​ " ​ + ​ " ​ \"​ macAddress​ \"​ : " ​ + ​ macAddress ​ + ​ "}"​ , ​ "AIzdfDef­x8ItqL7ab12qSMYChlRARwrXuyCqny0"​ , ​ new ​ Callback<CellId>() { 5: 6: ​ @Override 7: ​ public void ​ success(CellId cellId​ , ​ Response response) { 8: ​ latitude ​ = cellId.​ location​ .​ lat​ ; 9: ​ longitude ​ = cellId.​ location​ .​ lng​ ; 10: ​ txtLatLng​ .setText(​ latitude ​ + ​ ", " ​ + ​ longitude​ )​ ; 1!: ​ } 12: 13: ​ @Override 14: ​ public void ​ failure(RetrofitError error) {

15: Log.​ e​ (​ "TESTE"​ , ​ "ERRO: " ​ + error.getMessage())​ ; 16: ​ } 17: })​ ; 18:}

Na Listagem 8 veremos o método ​ getPositionByCellId. ​ O leitor peerceberá que a

semelhança é grande. Na verdade, a única diferença é nos parâmetros passados para o método ​ geolocate. ​ E, para ser mais detalhista ainda, só o primeiro parâmetro (o JSON) é alterado. A diferenã é que neste caso enviamos os dados da Cell ID e não do WiFi. Simples assim.

13


Listagem 8: método getPositionByCellId. public void ​ getPositionByCellId(View view){ RestAdapter retrofit = ​ new ​ RestAdapter.Builder() .setEndpoint(​ "https://www.googleapis.com"​ ) .build()​ ; ​ CellIdService service = retrofit.create(CellIdService.​ class​ )​ ; 1: ​ service.geolocate(​ "{​ \n​ " ​ + ​ " ​ \"​ cellTowers​ \"​ : [​ \n​ " ​ + ​ " {​ \n​ " ​ + ​ " ​ \"​ cellId​ \"​ : "​ +​ cid​ +​ ",​ \n​ " ​ + ​ " ​ \"​ locationAreaCode​ \"​ : "​ +​ lac​ +​ ",​ \n​ " ​ + ​ " ​ \"​ mobileCountryCode​ \"​ : "​ +​ networkOperator​ .substring(​ 0​ , ​ 3​ )+​ ",​ \n​ " ​ + ​ " ​ \"​ mobileNetworkCode​ \"​ : "​ +​ networkOperator​ .substring(​ 3​ )+​ "​ \n​ " ​ + ​ " }​ \n​ " ​ + ​ " ]​ \n​ " ​ + ​ "}"​ , ​ "AIzdfDef­x8ItqL7ab12qSMYChlRARwrXuyCqny0"​ , ​ new ​ Callback<CellId>() { ​ @Override ​ public void ​ success(CellId cellId​ , ​ Response response) { ​ txtLatLng​ .setText(cellId.​ location​ .​ lat ​ + ​ ", " ​ + cellId.​ location​ .​ lng​ )​ ; ​ } ​ @Override ​ public void ​ failure(RetrofitError error) {

Log.​ e​ (​ "TESTE"​ , ​ "ERRO: " ​ + error.getMessage())​ ; ​ } })​ ; }

14


Mostrando a localização no Mapa Para finalizar o artigo basta apenas mostrar como foi feito o proceso de mostrar a localização do usuário, segundo a Cell ID ou Wi Fi no Google Maps. Inicialmente, na classe ​ MainActivity ​ foi criado o método que responde a ação de clique no botão com o rótulo “​ See in Maps​ ”. Veja no trecho de código abaixo:

public void ​ seeInMaps(View v){ Intent intent = ​ new ​ Intent(​ this​ , ​ MapsActivity.​ class​ )​ ; ​ intent.putExtra(​ "latitude"​ , ​ latitude​ )​ ; ​ intent.putExtra(​ "longitude"​ , ​ longitude​ )​ ; ​ startActivity(intent)​ ; }

Na classe ​ MapsActivity, ​ que já foi gerada automaticamente pelo Android Studio foram

feitas pequenas alterações para mover o mapa ap ponto da latitude e longitude recebida e, além disso, colocar um marcador no ponto exato geolocalizado para a percepção do usuário ficar mais fácil. Veja na Listagem 9 como ficou o método ​ onCreate ​ da classe ​ MapsActivity:

Listagem 9: método onCreate da classe MapsActivity. protected void ​ onCreate(Bundle savedInstanceState) { ​ super​ .onCreate(savedInstanceState)​ ; ​ setContentView(R.layout.​ activity_maps​ )​ ; ​ setUpMapIfNeeded()​ ; ​ Bundle extras = getIntent().getExtras()​ ; ​ double ​ latitude = extras.getDouble(​ "latitude"​ )​ ; ​ double ​ longitude = extras.getDouble(​ "longitude"​ )​ ; ​ LatLng latLng = ​ new ​ LatLng(latitude​ , ​ longitude)​ ;

15


1: ​ mMap​ .animateCamera(CameraUpdateFactory.newLatLngZoom(latLng​ , ​ 17​ ))​ ; 2: ​ MarkerOptions mOpt = ​ new ​ MarkerOptions().title(​ "Você está aqui"​ ).position(latLng)​ ; 3: ​ mMap​ .addMarker(mOpt)​ ; }

Depois de receber os dados de latitude e longitude via ​ extras ​ da ​ Intent, ​ a linha 1 anima a câmera até o ponto indicado e já configura o nível de zoom para 17. Com isso a

aproximação facilita a visualizaçaõ dos nomes das ruas. E, finalmente, na linha 2 e 3 é criado um ​ MarkerOption ​ que é inserido no mapa e transformado em ​ Marlker ​ (muitas vezes também chamado de POI point of interest).

Conclusão O artigo demonstrou que é fácil inserir alternativas em uma aplicação LBS (​ Location

Based System​ ). O GPS é o meio mais preciso de geolocalizar um ponto no esfera terrestre, porém, nem sempre é o indicado. Sendo assim, saber usar WiFi e Cell ID é uma boa pedida para desenvolvedores Android. Por fim, resta dizer também que a Google Maps Geolocation API tornou essa possibilidade ainda mais real. Antes dela já existiam projetos, como o ​ Open CellID ​ que

permitia isso, porém, usando um framework próprio do ​ Google Play Service ​ o projeto fica mais limpo e mais integrado com as ferramentas do Android.

16


GEOLOCATION GPS BEYOND

GOOGLE MAPS GEOLOCATION WITH CELLID AND WIFI

Introduction A very large number of applications make use of some form of geolocation of users. The data are usually derived from the GPS or A-GPS, acronym for Global Positioning System and Assisted Global Positioning System. The hardware required to receive this information is present in almost all devices. It is a very common feature in smartphones. On the plus side of this form of geolocation we have very great precision. It is the best compared to other methods. The positioning system satellites is so effective that it was "copied" by the European Galileo system, the Russian Glonass and the Chinese Compass

17


However, there are some negatives. The GPS device consumes a lot of battery, and in some situations the application does not need to know exactly which street the user is just knowing the city and the neighborhood. The level of accuracy to the level of a few dozen meters from the GPS is unnecessary in this case. In addition, the GPS has some shadow points like tunnels, extremely dense woods, indoor environment, and has affected his quality when the weather is bad, as in storms and torrential rains. We are not saying that the GPS / A-GPS is bad, just that there are alternatives and, for some applications of concepts, even better than the famous triangulation antennas. In this article we will deal with two very usual ways. One of them is geolocation through the CellID (identification of a cell generated by a base station) and the other is via the access point where the smarpthone is connected to receive signal from a Wi-Fi network. In both cases will be used to API Google Maps Geolocation.

O que é Cell ID? Whenever your smartphone is receiving and making calls and/or allows you to surf the internet, it means that your device is within range of a base station from your service provider. Each antenna has a range. With the set of several antennas scattered throughout the cities we have a topology similar to the cell topology, so the name cell phone, smartphone grandfather. Every cell created by an antenna has a unique single identifier and this antenna is physically fixed in one place on earth. This position is defined by its latitude and longitude.

The proposed application The proposed application will consist of only two screens. mbém esteja em uma rede Wi-Fi mostrará o SSID e o Mac Address da mesma. The first shows the information of the cell id in which the smartphone is on and if the appliance is connected to a Wi-Fi network we will show the SSID and MAC address of it. See Figure 1 for the home screen:

18


Figura 1: Home screen of the application

By clicking on the option "Search position" the latitude and longitude will appear in a TextView located underneath the text "Result". After the geolocation data were collected you can click the "View Map" button. In figure 2 is shown the moment when the user seeks the position and the result is shown in place mentioned above:

19


Figura 2: Information about latitude and longitude being displayed to the user.

After searching the position you can click the "View Map" button. In this case, the application displays a screen shown in Figure 3.

20


Figura 3: Showing in the map informations about geoposition.

NOTE: the project is shared on github at the following address: https://github.com/ricardoogliari/GeolocationAPICellID.

Construction of layouts This text is aimed at programmers with at least a little experience on the Android platform. So I will not dwell on the detailed explanation of the construction of the layout. The​ activity_main​ is the layout of the main screen. See Listing 1:

21


Listagem 1: activity_main.xml <​ LinearLayout ​ xmlns:​ android​ =​ "http://schemas.android.com/apk/res/android" ​ xmlns:​ tools​ =​ "http://schemas.android.com/tools" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "match_parent" android​ :paddingLeft=​ "@dimen/activity_horizontal_margin" ​ android​ :paddingRight=​ "@dimen/activity_horizontal_margin" ​ android​ :paddingTop=​ "@dimen/activity_vertical_margin" ​ android​ :orientation=​ "vertical" ​ android​ :paddingBottom=​ "@dimen/activity_vertical_margin" tools​ :context=​ ".MainActivity"​ > <​ TextView ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "About CellID" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMnc" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMcc" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtLac" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtCid" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "getPositionByCellId" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Buscar Posiçao"​ /> <​ View ​ android​ :layout_marginTop=​ "20dp"

22


​ android​ :layout_marginBottom=​ "15dp" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "1dp" ​ android​ :background=​ "#000000"​ /> <​ TextView ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "About Wi­Fi" ​ android​ :id=​ "@+id/textView2" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtSSID" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ TextView ​ android​ :text=​ "­" ​ android​ :id=​ "@+id/txtMacAddress" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "getPositionByWiFi" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Buscar Posiçao"​ /> <​ View ​ android​ :layout_marginTop=​ "20dp" ​ android​ :layout_marginBottom=​ "15dp" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "1dp" ​ android​ :background=​ "#000000"​ /> <​ TextView ​ android​ :textStyle=​ "bold" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ android​ :textAppearance=​ "?android:attr/textAppearanceLarge" ​ android​ :text=​ "Resultado" ​ /> <​ TextView ​ android​ :text=​ "­"

23


​ android​ :id=​ "@+id/txtLatLng" ​ android​ :layout_width=​ "wrap_content" ​ android​ :layout_height=​ "wrap_content" ​ /> <​ Button ​ android​ :onClick=​ "seeInMaps" ​ android​ :layout_width=​ "match_parent" ​ android​ :layout_height=​ "wrap_content" ​ android​ :text=​ "Ver no mapa"​ /> </​ LinearLayout​ >

The map screen is exactly the same as that automatically generated by the Android Studio when a Google Map Activity is created. See Listing 2:

Listing 2: activity_maps.xml <​ fragment ​ xmlns:​ android​ =​ "http://schemas.android.com/apk/res/android"

xmlns:​ tools​ =​ "http://schemas.android.com/tools"

android​ :layout_width=​ "match_parent"

android​ :layout_height=​ "match_parent" ​ android​ :id=​ "@+id/map"

android​ :name=​ "com.google.android.gms.maps.SupportMapFragment" ​ />

Recovering CellID and Wi Fi information Before retrieving the information you need to set some user permissions on AndroidManifest.xml​ file, as follows:

<​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_COARSE_LOCATION" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.INTERNET" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_NETWORK_STATE" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_WIFI_STATE" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.WRITE_EXTERNAL_STORAGE" ​ /> <​ uses­permission android​ :name=​ "com.google.android.providers.gsf.permission.READ_GSERVICES" ​ /> <​ uses­permission ​ android​ :name=​ "android.permission.ACCESS_FINE_LOCATION" ​ />

24


The code of listing three is inserted into the onCreate method, which is part of the life cycle of the Activity. Recalling that any doubt the reader has access to the full code on GitHub. The address was passed at the end of the chapter that introduces the application to be built. To access data via positioning CellID is used the TelephonyManager class which may be instantiated by getSystemService method. As can be seen, this is a service of the own Android operating system. With the logical test IF done in the code, we make sure that we are working with a device that is in a GSM network. In this way, the desired information is: ● LAC: location area code; ● CID: Cell ID; ● MCC: mobile country code. This code is unique to each country. For Brazil the MCC

is 724. For a complete list visit this link: https://en.wikipedia.org/wiki/Mobile_country_code​ .

● MNC: mobile network code. A specific code for each operator. For a complete

listing visit this link: http://www.mcc-mnc.com/. In row 3 is called the getCellLocation method and passed his return to variable of the GsmCellLocation class.​ With this variable in hand you can make calls to getLac(), getCid() ​ and getNetworkOperator() methods. An important observation is the last method it required. The first three numbers refer to the MCC and the last three to MNC. On line 16 starts logic to fetch the data from the WiFi network if the smartphone is connected to one. The instance of WifiManager, which is also an native service of the operating system is initially. With this class in the hands the getConnectionInfo method is fired, bringing as answer an instance of the WifiInfo class. If the WifiInfo is not null, it means the device is connected to a WiFi network and can recover your SSID (on line 21) and your Mac Address (line 22).

25


Listing 3: code to recovery informations of the CellID and WiFi … 1:final ​ TelephonyManager t = (TelephonyManager)getSystemService(Context.​ TELEPHONY_SERVICE​ )​ ; 2:if ​ (t.getPhoneType() == TelephonyManager.​ PHONE_TYPE_GSM​ ) { 3: ​ final ​ GsmCellLocation location = (GsmCellLocation) t.getCellLocation()​ ; 4: ​ if ​ (location != ​ null​ ) { 5: ​ lac ​ = location.getLac()​ ; 6: ​ cid ​ = location.getCid()​ ; 7: 8: ​ txtLac​ .setText(​ "Lac: " ​ + ​ lac​ )​ ; 9: ​ txtCid​ .setText(​ "Cid: " ​ + ​ cid​ )​ ; 10: ​ networkOperator ​ = t.getNetworkOperator()​ ; 11: ​ txtMcc​ .setText(​ "MCC: " ​ + ​ networkOperator​ .substring(​ 0​ , ​ 3​ ))​ ;​ //MCCMNC 12: ​ txtMnc​ .setText(​ "MNC: " ​ + ​ networkOperator​ .substring(​ 3​ ))​ ;​ //MCCMNC 13: ​ } 14:} 15: 16:WifiManager mainWifi = (WifiManager) getSystemService(Context.​ WIFI_SERVICE​ )​ ; 17:WifiInfo currentWifi = mainWifi.getConnectionInfo()​ ; 18:if​ (currentWifi != ​ null​ ) 19:{ 20: ​ macAddress ​ = currentWifi.getMacAddress()​ ; 21: ​ txtSSID​ .setText(currentWifi.getSSID())​ ; 22: ​ txtMacAddress​ .setText(​ macAddress​ )​ ; 23:} ...

Georeferencing the device with Google Maps Geolocation API For a more detailed reading on the API indicated reading the following document: The Google Maps Geolocation API: https://developers.google.com/maps/documentation/geolocation/intro​ . 26


The use of API is used JSON format both sending the request as much in response. This fact was another reason we use the Retrofit framework for HTTP requests. So it was created two Java classes to mirror the response of the Google API, which resembles the Listing 4:

Listing 4: return example of the API. { "location"​ ​ :{

"lat"​ ​ :​ 51.0​ , "lng"​ ​ : -​ 0.1

}, }

"accuracy"​ ​ :​ 1200.4

The listing 5 show the two classes that mirror the above JSON:

Listing 5: Location class. public class ​ Location { ​ double ​ lat​ ; ​ double ​ lng​ ; } public class ​ CellId { ​ float ​ accuracy​ ; ​ Location ​ location​ ; }

The framework Retrofit also requires the creation of a interface which, through the use of the annotations of the library that indicate the endpoints that are used by HTTP requests. See in Listing 6 as was our interface:

Listagem 6: CellIdService interface.

27


public interface ​ CellIdService { ​ @POST​ (​ "/geolocation/v1/geolocate"​ ) ​ void ​ geolocate( @Body ​ String body​ , @Query​ (​ "key"​ ) String key​ , Callback<CellId> callback)​ ; }

In the class MainActivity was coded two methods that responds to click on the buttons of the screen and indicate that the user wants to know the location through the data from the cell ID or WiFi. Initially, we will analyze the get getPositionByWiFi() method. See Listing 7:: On the first line is configured endpoint for RestAdapter, of the Retrofit library. It is required to, in line 3, create the instance of the interface we've seen before in Listing 6. With the service variable simply call the geolocate method and pass the parameters configured on the interface and that are required by Google Geolocation API. In the first parameter we have the JSON that sends the only required parameter, the macAddress. To read all the parameters that we can pass we have indicated the API documentation of the site in the previous steps. The second parameter is the api key, which can be created in the Google APIs Console. And finally, we have a Callback we will tell you when the request was made and there have been a server response, be it success or error. On line 7 is encoded the callback method in case of success. In this case, simply restore the data of latitude and longitude that are ready in the instance of the class CellID. Thank Retrofit for it.

Listagem 7: getPositionByWiFi method. public void ​ getPositionByWiFi(View view){ 1: RestAdapter retrofit = ​ new ​ RestAdapter.Builder() .setEndpoint(​ "https://www.googleapis.com"​ ) .build()​ ;

28


2: 3: ​ CellIdService service = retrofit.create(CellIdService.​ class​ )​ ; 4: ​ service.geolocate(​ "{​ \n​ " ​ + ​ " ​ \"​ macAddress​ \"​ : " ​ + ​ macAddress ​ + ​ "}"​ , ​ "AIzdfDef­x8ItqL7ab12qSMYChlRARwrXuyCqny0"​ , ​ new ​ Callback<CellId>() { 5: 6: ​ @Override 7: ​ public void ​ success(CellId cellId​ , ​ Response response) { 8: ​ latitude ​ = cellId.​ location​ .​ lat​ ; 9: ​ longitude ​ = cellId.​ location​ .​ lng​ ; 10: ​ txtLatLng​ .setText(​ latitude ​ + ​ ", " ​ + ​ longitude​ )​ ; 1!: ​ } 12: 13: ​ @Override 14: ​ public void ​ failure(RetrofitError error) {

15: Log.​ e​ (​ "TESTE"​ , ​ "ERRO: " ​ + error.getMessage())​ ; 16: ​ } 17: })​ ; 18:}

In Listing 8 will see the getPositionByCellID method. The reader realizes that the similarity is great. In fact, the only difference is in the parameters passed to geolocate method. And to be even more detail, only the first parameter (JSON) is changed. The difference is that in this case we send the data from the Cell ID and WiFi not. Simple as that.

Listagem 8: getPositionByCellId method. public void ​ getPositionByCellId(View view){ RestAdapter retrofit = ​ new ​ RestAdapter.Builder() .setEndpoint(​ "https://www.googleapis.com"​ ) .build()​ ; ​ CellIdService service = retrofit.create(CellIdService.​ class​ )​ ; 1: ​ service.geolocate(​ "{​ \n​ " ​ + ​ " ​ \"​ cellTowers​ \"​ : [​ \n​ " ​ + ​ " {​ \n​ " ​ + ​ " ​ \"​ cellId​ \"​ : "​ +​ cid​ +​ ",​ \n​ " ​ + ​ " ​ \"​ locationAreaCode​ \"​ : "​ +​ lac​ +​ ",​ \n​ " ​ +

29


​ " ​ \"​ mobileCountryCode​ \"​ : "​ +​ networkOperator​ .substring(​ 0​ , ​ 3​ )+​ ",​ \n​ " ​ + ​ " ​ \"​ mobileNetworkCode​ \"​ : "​ +​ networkOperator​ .substring(​ 3​ )+​ "​ \n​ " ​ + ​ " }​ \n​ " ​ + ​ " ]​ \n​ " ​ + ​ "}"​ , ​ "AIzdfDef­x8ItqL7ab12qSMYChlRARwrXuyCqny0"​ , ​ new ​ Callback<CellId>() { ​ @Override ​ public void ​ success(CellId cellId​ , ​ Response response) { ​ txtLatLng​ .setText(cellId.​ location​ .​ lat ​ + ​ ", " ​ + cellId.​ location​ .​ lng​ )​ ; ​ } ​ @Override ​ public void ​ failure(RetrofitError error) {

Log.​ e​ (​ "TESTE"​ , ​ "ERRO: " ​ + error.getMessage())​ ; ​ } })​ ; }

Showing the location in the map Finally the article need only show how it was done the proceso to show the user's location, according to the Cell ID or Wi Fi on Google Maps. Initially, the MainActivity class created the method that responds to action click in the button labeled "See in Maps". See the code snippet below:

public void ​ seeInMaps(View v){ Intent intent = ​ new ​ Intent(​ this​ , ​ MapsActivity.​ class​ )​ ; ​ intent.putExtra(​ "latitude"​ , ​ latitude​ )​ ; ​ intent.putExtra(​ "longitude"​ , ​ longitude​ )​ ; ​ startActivity(intent)​ ; }

30


In MapActivity class, which has been automatically generated by the Android Studio minor changes were made to move the map to the point of latitude and longitude received and also place a marker at the exact geopoint to the user's perception easier. See in Listing 9 as was the onCreate method of MapsActivity class:

Listagem 9: onCreate method of the MapsActivity class. protected void ​ onCreate(Bundle savedInstanceState) { ​ super​ .onCreate(savedInstanceState)​ ; ​ setContentView(R.layout.​ activity_maps​ )​ ; ​ setUpMapIfNeeded()​ ; ​ Bundle extras = getIntent().getExtras()​ ; ​ double ​ latitude = extras.getDouble(​ "latitude"​ )​ ; ​ double ​ longitude = extras.getDouble(​ "longitude"​ )​ ; ​ LatLng latLng = ​ new ​ LatLng(latitude​ , ​ longitude)​ ; 1: ​ mMap​ .animateCamera(CameraUpdateFactory.newLatLngZoom(latLng​ , ​ 17​ ))​ ; 2: ​ MarkerOptions mOpt = ​ new ​ MarkerOptions().title(​ "Você está aqui"​ ).position(latLng)​ ; 3: ​ mMap​ .addMarker(mOpt)​ ; }

After receiving the data of latitude and longitude by extra of Intent, line 1 animates the camera to the selected point and has set the zoom level to 17. With this approach facilitates the visualization of street names. And finally, in line with 2 and 3 creates a MarkerOption that is inserted into the map and turned into marker (often also called POI point of interest).

31


Conclusion The article demonstrates that it is easy to insert alternative in a LBS application (Location Based System). GPS is the most accurate means of geolocalizar a point in the earthly sphere, however, it is not always indicated. Thus, knowing how to use WiFi and Cell ID is a good choice for Android developers. Finally, it is importante to say that Google Maps Geolocation API made this possibility even more real. Before it already existed projects, such as Open CellID, allowing this, however, using its own framework Google Play Service the design is cleaner and more integrated with the Android tools.

32


Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.