Как реализовать отслеживание местоположения андроид устройства на своем сайте
Занимаюсь грузоперевозками на своей газели, и у меня родилась идея сделать функцию отслеживания своего местоположения для всех посетителей своего сайта. Это очень удобно для тех, кто следит за своим грузом или тех, кто ждет машину. У меня получилось. И в этом посте я расскажу вам как реализовать эту функцию, которая, кстати, обойдется совсем недорого. Статья предназначена для людей, которые почти не имеют опыта в сфере создания сайтов и программировании в целом.
Вам понадобится:
Редактор Visual Studio Code
Среда разработки Android Studio
Аккаунт Гугл для API Google Maps
Доменное имя и хостинг с базой данных SQL
Обычный телефон на андроиде
Для начала рассмотрим схему взаимодействия всех элементов.
После того как пользователь заходит на сайт, активируется подключенный в index.html файл myJsCode.js, в котором каждые 2 секунды идет обращение в google maps. В google maps передаются координаты текущего местоположения, которые хранятся в базе данных sql. Google maps возвращает карту с маркером, установленным в точке, соответствующей переданным координатам. Считывание новых координат из базы данных осуществляется в myJsCode.js через coordinatesToBrowser.php также каждые 2 секунды. Запись координат в базу данных осуществляет Android приложение через coordinatesFromAndroid.php каждые 2 секунды при условии движения андроид устройства. Пользователь приложения может включать и выключать определение координат. Далее соберем эту схему.
1. Для инициализации google maps потребуются API ключ. Для его получения переходим на https://cloud.google.com/maps-platform
Нажимаем Get started.
Создаем новый проект.
В созданном проекте выбираем Maps JavaScript API.
Нажимаем Enable.
Жмем на гамбургер → APIs & Services → Credentials.
Нажимаем на +Create Credentials
И мы получили Api key
2. Создаем файл index.html и style.css и myJsCode.js. Писать код будем в редакторе Visual Studio Code, скачиваем его по ссылке https://code.visualstudio.com/
index.html В 71 строчке меняете API key на свой. В 69 строчке подключается файл myJsCode.js, который мы напишем ниже. В 53 строчке в блоке div будет располагаться карта с маркером.
Название сайта
Далее пишем style.css В этом файле оставляем все как есть. Здесь уже предусмотрена адаптация под мобильные устройства с помощью media запросов.
*
{
margin: 0;
padding: 0;
}
.box{
max-width:1560px;
margin: 0px auto;
padding: 0px 0px;
}
header{
position: fixed;
background-color: white;
padding-top: 10px;
height:60px;
width: 100%;
box-shadow: 7px 7px 5px rgba(63, 62, 62, 0.6); /* Тень */
}
.content{
padding-top: 100px;
}
.logo{
float:left;
margin-left: 10px;
}
.logoTitle{
float:left;
margin-top: 10px;
margin-left: 10px;
text-decoration: none;
font-family: sans-serif;
font-size: 20px;
color: #000;
}
.phoneAndName{
text-align: center;
color: #000;
margin-bottom: 15px;
}
.imgphone{
text-decoration: none;
}
.telephone{
text-decoration: none;
font-family: sans-serif;
font-size: 30px;
color: #000;
}
.yourName{
color: #030c01;
font-size: 30px;
}
.descriptionYourBuisless{
text-align: center;
font-size: 20px;
color: #000;
margin-bottom: 20px;
padding: 0px 10px;
}
.descriptionHeader2{
justify-content: center;
display:flex;
}
.Header2{
color: #000;
font-size: 20px;
padding-right: 10px;
padding-left: 40px;
}
.Header2Text{
padding-right: 20px;
color: #000;
font-size: 20px;
margin-bottom: 20px;
}
.photoCargo{
margin: auto;
width:55%;
height:100%;
color: #000;
}
.row1{
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
margin-right: 20px;
}
.row2{
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
}
li{
font-size: 20px;
color: #000;
}
#map-canvas{
height:600px;
width: 100%;
margin-top: 20px;
}
.descriptionHeader3{
margin-top: 20px;
justify-content: center;
display:flex;
}
.Header3{
color: #000;
font-size: 20px;
padding-right: 10px;
padding-left: 40px;
}
.Header3Text{
padding-right: 20px;
color: #000;
font-size: 20px;
margin-bottom: 20px;
}
footer{
text-align: center;
font-size: 14px;
color: #000;
margin-top: 20px;
margin-bottom: 20px;
padding-left: 5px;
padding-right: 5px;
}
@media(min-width: 1560px){
.adPointers{
justify-content: center;
display:flex;
}
}
@media(max-width: 1560px){
.adPointers{
justify-content: center;
display:flex;
}
.box{
max-width:1460px;
}
}
@media(max-width: 1400px){
.box{
max-width:1360px;
}
.photoCargo{
width:60%;
}
}
@media(max-width: 1200px){
.box{
max-width:970px;
}
.photoCargo{
width:65%;
}
}
@media(max-width: 992px){
.box{
max-width:850px;
}
.photoCargo{
width:80%;
}
}
@media(max-width: 767px){
.box{
max-width:none;
}
.photoCargo{
width:90%;
}
}
@media(max-width: 695px){
.photoCargo{
width:95%;
}
}
@media(max-width: 450px){
.adPointers{
flex-direction: column;
margin-left: 100px;
}
}
myJsCode.js В 1 и 2 строчке задаются координаты по умолчанию. В 6 строчке функция инициализации google maps. Внутри функции setTimeout каждые 2 секунды идет считывание координат из coordinatesToBrowser.php и передача новых координат в google maps. Файл coordinatesToBrowser.php напишем ниже.
window.lat = 37.7850;
window.lng = -122.4383;
var map;
var mark;
var initialize = function () {
map = new google.maps.Map(document.getElementById('map-canvas'), { center: { lat: lat, lng: lng }, zoom: 12 });
mark = new google.maps.Marker({ position: { lat: lat, lng: lng }, map: map });
};
var compare=0;
let timerId = setTimeout(function tick() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'coordinatesToBrowser.php', true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var jsonObj=JSON.parse(xhr.responseText);
const arr = Object.keys(jsonObj).map((key) => [key, jsonObj[key]]);
lat= parseFloat(arr[0][1]);
lng= parseFloat(arr[1][1]);
if(compare!=lat){
map.setCenter({ lat: lat, lng: lng, alt: 0 });
mark.setPosition({ lat: lat, lng: lng, alt: 0 });
}
compare=lat;
}
}
xhr.send(null);
timerId = setTimeout(tick, 2000);
}, 2000);
3. Все файлы заливаете на свой хост. Далее переходим в php MyAdmin. Создаете базу данных или используете уже созданную автоматически. В ней создаете таблицу под названием coordinates. В этой таблице создаете 3 столбца: id, latitude и longitude как на изображении.
4. Далее в папку сайта на хостинг помещаете два php файла, которые будут взаимодействовать с базой данных, а именно таблицей coordinates. Файл coordinatesToBrowser.php будет считывать координаты из базы, а coordinatesFromAndroid.php будет записывать переданные android устройством координаты в базу данных.
coordinatesToBrowser.php В 15 строчке происходит выборка из созданной нами таблицы coordinates, а именно из столбцов latitude и longitude. Подключение к самой базе данных прокомментировано в коде.
fetch_assoc();
$lat=$row['latitude'];
$lon=$row['fetch_assoc();
$lat=$row['latitude'];
$lon=$row['longitude'];
$cart = array(
"latitude" => $lat,
"longitude" => $lon,
);
echo json_encode( $cart );
} else {
// echo 'Произошла ошибка: ' . mysqli_error($link) . '
';
}
?>];
$cart = array(
"latitude" => $lat,
"longitude" => $lon,
);
echo json_encode( $cart );
} else {
// echo 'Произошла ошибка: ' . mysqli_error($link) . '
';
}
?>
coordinatesFromAndroid.php. В 22 строчке идет перезапись столбцов новыми значениями.
Данные успешно добавлены в таблицу.';
} else {
// echo 'Произошла ошибка: ' . mysqli_error($link) . '
';
}
}
?>
5. Заключительным этапом мы создадим андроид приложение, которое будет передавать координаты каждые 2 секунды во время движения. Приложение запускает сервис, который запрашивает текущие координаты у встроенного gps приемника. Для работы в фоновом режиме необходимо стороннее приложение, например такси или карты, при работе которых в верхней панели отображается 'самолетик' (геолокация). Несмотря на то, что сервис очень живучий в приложении предусмотрен менеджер, перезапускающий сервис каждые 30 секунд на случай, если андроид очистит от него память.
Итак скачиваем и устанавливаем среду разработки приложений Android Studio.
Создаете новый проект, выбрав empty Activity
Называете приложение и выбираете язык Java
В баре слева выбираете App → res → layout открываете файл activity_main.xml и меняете его содержимое на:
activity_main.xml Файл представляет собой внешний вид страницы приложения. На нем расположены кнопки Получить координаты и Сбросить координаты, а также текстовые поля для отображения координат и региона.
Далее App →manifest открываете файл AndroidManifest.xml и меняете его содержимое на:
AndroidManifest.xml Файл несет в себе информацию о приложении для операционной системы. В него включены данные об активити, сервисах, разрешениях и другом. Оставляете свой package=«yourpackage» строчка 3.
Далее снова App → res → values открываете файл colors.xml и меняете его содержимое на:
colors.xml В файле задаются цвета элементов.
#6200EE
#3700B3
#03DAC5
#808040
#868602
Далее Gradle Scripts открываете файл build.grande (Module app) и меняете его содержимое на:
build.grande (Module app) В файле информация о сборке проекта и его зависимостях. В строчке 5 оставляете свой applicationId.
apply plugin: 'com.android.application'
android { compileSdkVersion 30 buildToolsVersion "30.0.2"
defaultConfig {
applicationId "yourPackage"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
useLibrary 'org.apache.http.legacy' // Библиотека для HTTP Client
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
Далее снова App → Java → YourPackage открываете файл MainActivity.java и меняете его содержимое на:
MainActivity.java В этом файле код основного активити. Строку 1 оставляете свою. В файле проходит инициализация кнопок, текстовых полей. Инициализация AlarmManager, который 'пробуждает' основной сервис. Обработка нажатия кнопок. Обработка разрешений от пользователя. Прием и отображение координат и региона. Запуск сервиса GoogleService, который запрашивает gps координаты осуществляется в обработчике нажатия кнопки btn_start. Сброс координат в значение по умолчанию осуществляется в обработчике кнопки btn_finish. Сам GoogleService и AlarmManager опишем ниже.
package yourpackage;
import android.Manifest;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
public class MainActivity extends Activity {
public static boolean stsrtFinish = true;
Button btn_start;
Button btn_finish;
private static final int REQUEST_PERMISSIONS = 100;
boolean boolean_permission;
TextView tv_latitude, tv_longitude, tv_address, tv_area, tv_locality;
SharedPreferences mPref;
SharedPreferences.Editor medit;
Double latitude, longitude;
Geocoder geocoder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
final Intent intent = new Intent(this, MyAlarmReceiver.class);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
Calendar time = Calendar.getInstance();
time.setTimeInMillis(System.currentTimeMillis());
time.add(Calendar.SECOND, 5);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, time.getTimeInMillis(),3000, pendingIntent);
btn_start = (Button) findViewById(R.id.btn_start);
btn_finish = (Button) findViewById(R.id.btn_finish);
tv_address = (TextView) findViewById(R.id.tv_address);
tv_latitude = (TextView) findViewById(R.id.tv_latitude);
tv_longitude = (TextView) findViewById(R.id.tv_longitude);
tv_area = (TextView) findViewById(R.id.tv_area);
tv_locality = (TextView) findViewById(R.id.tv_locality);
geocoder = new Geocoder(this, Locale.getDefault());
mPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
medit = mPref.edit();
btn_finish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stsrtFinish = false;
alarmMgr.cancel(pendingIntent);
}
});
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (boolean_permission) {
medit.putString("service", "service").commit();
Intent intent = new Intent(getApplicationContext(), GoogleService.class);
startService(intent);
} else {
Toast.makeText(getApplicationContext(), "Please enable the gps", Toast.LENGTH_SHORT).show();
}
}
});
fn_permission();
}
private void fn_permission() {
if ((ContextCompat.checkSelfPermission(getApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)) {
if ((ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION))) {
} else {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION
},
REQUEST_PERMISSIONS);
}
} else {
boolean_permission = true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_PERMISSIONS: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
boolean_permission = true;
} else {
Toast.makeText(getApplicationContext(), "Please allow the permission", Toast.LENGTH_LONG).show();
}
}
}
}
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
latitude = Double.valueOf(intent.getStringExtra("latutide"));
longitude = Double.valueOf(intent.getStringExtra("longitude"));
List addresses = null;
try {
addresses = geocoder.getFromLocation(latitude, longitude, 1);
String cityName = addresses.get(0).getAddressLine(0);
String stateName = addresses.get(0).getAddressLine(1);
String countryName = addresses.get(0).getAddressLine(2);
tv_area.setText(addresses.get(0).getAdminArea());
tv_locality.setText(stateName);
tv_address.setText(countryName);
} catch (IOException e1) {
e1.printStackTrace();
}
tv_latitude.setText(latitude + "");
tv_longitude.setText(longitude + "");
tv_address.getText();
}
};
@Override
protected void onResume() {
super.onResume();
registerReceiver(broadcastReceiver, new IntentFilter(GoogleService.str_receiver));
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(broadcastReceiver);
}
}
Далее Далее снова App → Java → YourPackage нажимаете правую кнопку мыши, во всплывающем меню создаете новый Java class и называете его GoogleService.java.
GoogleService.java Строку 1 оставляете свою. Файл представляет собой сервис, работающий в фоновом режиме, который принимает gps координаты с помощью LocationManager. В методе onLocationChanged идет передача координат на ваш сервер HttpPost post = new HttpPost («https://yourSite.ru/coordinatesFromAndroid.php») Адрес сайта поменяйте на свой строка 90.
package yourpackage;
import android.Manifest;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import org.apache.http.util.EntityUtils;
import com.google.gson.JsonObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class GoogleService extends Service implements LocationListener {
private JsonObject message;
boolean isGPSEnable = false;
double latitude, longitude;
LocationManager locationManager;
public static String str_receiver = "servicetutorial.service.receiver";
Intent intent;
List params;
public GoogleService() {
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
intent = new Intent(str_receiver);
message = new JsonObject();
params = new ArrayList();
locationManager = (LocationManager) getApplicationContext().getSystemService(LOCATION_SERVICE);
isGPSEnable = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 10, this);
}
@Override
public void onLocationChanged(Location location) {
params = new ArrayList();
if (isGPSEnable) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location!=null) {
Log.e("location!=null", "location!=null");
message.addProperty("lat", location.getLatitude());
message.addProperty("lng", location.getLongitude());
if(MainActivity.stsrtFinish) {
params.add(new BasicNameValuePair("Latitude", String.valueOf(location.getLatitude())));
params.add(new BasicNameValuePair("Longitude", String.valueOf(location.getLongitude())));
}
else{
params.add(new BasicNameValuePair("Latitude", "37.7850"));
params.add(new BasicNameValuePair("Longitude", "-122.4383"));
}
new Thread(new Runnable() {
public void run() {
try {
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, 10000);
HttpConnectionParams.setSoTimeout(httpParameters, 10000);
HttpClient httpClientpost = new DefaultHttpClient(httpParameters);
HttpPost post = new HttpPost("https://yourSite.ru/coordinatesFromAndroid.php");
UrlEncodedFormEntity ent = new UrlEncodedFormEntity(params, HTTP.UTF_8);
post.setEntity(ent);
HttpResponse responsePOST = httpClientpost.execute(post);
HttpEntity resEntity = responsePOST.getEntity();
String getresponse = EntityUtils.toString(resEntity);
//Response from the server
Log.e("response",getresponse);
}
catch (IOException e) {
e.printStackTrace();
Log.e("catch","catch");
}
}
}).start();
latitude = location.getLatitude();
longitude = location.getLongitude();
fn_update(location);
}
}
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {}
@Override
public void onProviderEnabled(String provider) { }
@Override
public void onProviderDisabled(String provider) { }
private void fn_update(Location location){
intent.putExtra("latutide",location.getLatitude()+"");
intent.putExtra("longitude",location.getLongitude()+"");
sendBroadcast(intent);
}
}
Далее Далее снова App → Java → YourPackage нажимаете правую кнопку мыши, во всплывающем меню создаете новый Java class и называете его MyAlarmReceiver.java.
MyAlarmReceiver.java Строку 1 оставляете свою. Код в этом файле запускается каждые 30 секунд. Таким образом происходит перезапуск GoogleService, в случае если сервис перестал работать.
package yourpackage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class MyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent){
context.startService(new Intent(context, GoogleService.class));
}
}
Далее включаете на своем андроид телефоне режим разработчика, подключаетесь через usb к компьютеру, в окошке Devices отобразится название вашего телефона. Нажимаете кнопку пуск. Начнется сборка и установка приложения на телефон. Если все прошло без ошибок, значит ваш сервис работает так же как и мой. Работу сервиса можете увидеть на сайте https://gaselka71.ru
Для тех, кому нравится видеоформат можете посмотреть плейлист по этой теме на моем канале:
Всем спасибо за внимание. Желаю удачи!