Статьи

Учебник по оповещениям о близости Android

Смартфоны захватывают мир мобильных устройств, это факт. Поскольку устройства GPS обычно встроены в эти телефоны, уже наблюдается заметный рост числа приложений, в которых используются предлагаемые функции географического позиционирования. Тип этих приложений — один из Служб определения местоположения , где служба использует знания о том, где мобильный пользователь расположен в глобальном масштабе. Довольно распространенными являются также приложения, использующие геокодирование (поиск связанных географических координат по географическим данным, таким как адрес улицы) и обратное геокодирование (предоставление информации на основе заданных координат). Еще одним аспектом этого типа приложений является создание оповещений о близости. Как следует из их названия, это оповещения, которые генерируются, когда пользователь физически находится рядом с определенной точкой интереса (POI) . В ближайшие годы оповещение о близости станет «горячей» вещью, поскольку многие приложения будут использовать их, и наиболее ярким примером является целевая реклама. В этом уроке я собираюсь показать вам, как воспользоваться встроенными в Android возможностями оповещения о близости.

Прежде чем мы начнем, было бы полезно прочитать вводные статьи о приложении на основе определения местоположения и / или геокодировании. Возможно, вы захотите взглянуть на некоторые мои предыдущие учебные пособия, такие как «Приложение для определения местоположения Android — GPS-местоположение» и «Обратное геокодирование Android с Yahoo API — PlaceFinder» . Еще одна вещь, на которую стоит обратить внимание, это то, что этот учебник был вдохновлен очень классным учебником под названием «Разработка предупреждений о близости для мобильных приложений с использованием платформы Android» . Это учебник из четырех частей, который в некоторых моментах немного усложняется и может запугать новичка. По этой причине я решил предоставить более короткий и простой учебник.

Мы создадим простое приложение, которое хранит координаты пользователя для точки, которая его интересует, а затем предоставляет уведомление, когда пользователь приближается к этой точке. Координаты извлекаются по запросу, когда пользователь находится в этой точке.

Мы начнем с создания нового проекта Eclipse с именем «AndroidProximityAlertProject» в нашем случае. Мы также создаем основной вид деятельности для нашего приложения под названием «ProxAlertActivity». Вот как будет выглядеть главная страница приложения:

Вот файл объявления для основного макета пользовательского интерфейса с именем «main.xml»:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="utf-8"?>
 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
>
 
    <EditText
        android:id="@+id/point_latitude"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dip"
        android:layout_marginRight="25dip"
    />
     
    <EditText
        android:id="@+id/point_longitude"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="25dip"
        android:layout_marginRight="25dip"
    />
         
    <Button
        android:id="@+id/find_coordinates_button"
        android:text="Find Coordinates"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
    />
     
    <Button
        android:id="@+id/save_point_button"
        android:text="Save Point"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
    />
     
</LinearLayout>

Давайте теперь начнем с интересного. Прежде всего нам нужна ссылка на класс LocationManager , который обеспечивает доступ к системным службам определения местоположения. Это делается с помощью вызова метода getSystemService нашей деятельности. Затем мы можем использовать метод requestLocationUpdates , чтобы запрашивать уведомления при изменении местоположения пользователя. Это не является обязательным требованием при разработке оповещений о близости, но я буду использовать его здесь, чтобы рассчитать расстояние между точкой интереса и текущим местоположением пользователя. В любой момент времени мы можем вызвать метод getLastKnownLocation и получить последнее известное местоположение конкретного поставщика, в нашем случае это устройство GPS. Наконец, мы будем использовать метод addProximityAlert, который можно использовать для установки оповещения о близости для местоположения, заданного конкретными координатами (широта, долгота) и данным радиусом. Мы также можем при желании определить срок действия этого оповещения, если мы хотим отслеживать оповещение в течение определенного периода времени. Также может быть предоставлено PendingIntent , которое будет использоваться для генерации Intent для срабатывания при обнаружении входа или выхода из области предупреждения.

Все это переводится в код следующим образом:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.javacodegeeks.android.lbs;
 
import java.text.DecimalFormat;
import java.text.NumberFormat;
 
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
 
public class ProxAlertActivity extends Activity {
     
    private static final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 1; // in Meters
    private static final long MINIMUM_TIME_BETWEEN_UPDATE = 1000; // in Milliseconds
     
    private static final long POINT_RADIUS = 1000; // in Meters
    private static final long PROX_ALERT_EXPIRATION = -1;
 
    private static final String POINT_LATITUDE_KEY = "POINT_LATITUDE_KEY";
    private static final String POINT_LONGITUDE_KEY = "POINT_LONGITUDE_KEY";
     
    private static final String PROX_ALERT_INTENT =
         "com.javacodegeeks.android.lbs.ProximityAlert";
     
    private static final NumberFormat nf = new DecimalFormat("##.########");
     
    private LocationManager locationManager;
     
    private EditText latitudeEditText;
    private EditText longitudeEditText;
    private Button findCoordinatesButton;
    private Button savePointButton;
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
         
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
         
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
 
        locationManager.requestLocationUpdates(
                        LocationManager.GPS_PROVIDER,
                        MINIMUM_TIME_BETWEEN_UPDATE,
                        MINIMUM_DISTANCECHANGE_FOR_UPDATE,
                        new MyLocationListener()
        );
         
        latitudeEditText = (EditText) findViewById(R.id.point_latitude);
        longitudeEditText = (EditText) findViewById(R.id.point_longitude);
        findCoordinatesButton = (Button) findViewById(R.id.find_coordinates_button);
        savePointButton = (Button) findViewById(R.id.save_point_button);
         
        findCoordinatesButton.setOnClickListener(new OnClickListener() {           
            @Override
            public void onClick(View v) {
                populateCoordinatesFromLastKnownLocation();
            }
        });
         
        savePointButton.setOnClickListener(new OnClickListener() {           
            @Override
            public void onClick(View v) {
                saveProximityAlertPoint();
            }
        });
        
    }
     
    private void saveProximityAlertPoint() {
        Location location =
            locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if (location==null) {
            Toast.makeText(this, "No last known location. Aborting...",
                Toast.LENGTH_LONG).show();
            return;
        }
        saveCoordinatesInPreferences((float)location.getLatitude(),
               (float)location.getLongitude());
        addProximityAlert(location.getLatitude(), location.getLongitude());
    }
 
    private void addProximityAlert(double latitude, double longitude) {
         
        Intent intent = new Intent(PROX_ALERT_INTENT);
        PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
         
        locationManager.addProximityAlert(
            latitude, // the latitude of the central point of the alert region
            longitude, // the longitude of the central point of the alert region
            POINT_RADIUS, // the radius of the central point of the alert region, in meters
            PROX_ALERT_EXPIRATION, // time for this proximity alert, in milliseconds, or -1 to indicate no expiration
            proximityIntent // will be used to generate an Intent to fire when entry to or exit from the alert region is detected
       );
         
       IntentFilter filter = new IntentFilter(PROX_ALERT_INTENT); 
       registerReceiver(new ProximityIntentReceiver(), filter);
        
    }
 
    private void populateCoordinatesFromLastKnownLocation() {
        Location location =
            locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if (location!=null) {
            latitudeEditText.setText(nf.format(location.getLatitude()));
            longitudeEditText.setText(nf.format(location.getLongitude()));
        }
    }
     
    private void saveCoordinatesInPreferences(float latitude, float longitude) {
        SharedPreferences prefs =
           this.getSharedPreferences(getClass().getSimpleName(),
                           Context.MODE_PRIVATE);
        SharedPreferences.Editor prefsEditor = prefs.edit();
        prefsEditor.putFloat(POINT_LATITUDE_KEY, latitude);
        prefsEditor.putFloat(POINT_LONGITUDE_KEY, longitude);
        prefsEditor.commit();
    }
     
    private Location retrievelocationFromPreferences() {
        SharedPreferences prefs =
           this.getSharedPreferences(getClass().getSimpleName(),
                           Context.MODE_PRIVATE);
        Location location = new Location("POINT_LOCATION");
        location.setLatitude(prefs.getFloat(POINT_LATITUDE_KEY, 0));
        location.setLongitude(prefs.getFloat(POINT_LONGITUDE_KEY, 0));
        return location;
    }
     
    public class MyLocationListener implements LocationListener {
        public void onLocationChanged(Location location) {
            Location pointLocation = retrievelocationFromPreferences();
            float distance = location.distanceTo(pointLocation);
            Toast.makeText(ProxAlertActivity.this,
                    "Distance from Point:"+distance, Toast.LENGTH_LONG).show();
        }
        public void onStatusChanged(String s, int i, Bundle b) {           
        }
        public void onProviderDisabled(String s) {
        }
        public void onProviderEnabled(String s) {           
        }
    }
     
}

В методе onCreate мы подключаем менеджер местоположений к пользовательскому классу, который реализует интерфейс LocationListener и позволяет получать уведомления об изменениях местоположения с помощью метода onLocationChanged . Мы увидим, как обрабатывать обновления позже. Мы также находим различные UI-виджеты и прикрепляем OnClickListener s к кнопкам.

Когда пользователь хочет найти свои текущие координаты, вызывается метод «populateCoordinatesFromLastKnownLocation». Внутри этого мы используем метод getLastKnownLocation и получаем объект Location . EditText затем заполняется полученной информацией о местоположении.

Точно так же, когда пользователь хочет сохранить точку и предоставить для нее оповещения («saveProximityAlertPoint»), сначала извлекается информация о местоположении. Затем мы сохраняем информацию о широте и долготе как данные о предпочтениях, используя класс SharedPreferences и, более конкретно, SharedPreferences.Editor . Наконец, мы создаем PendingIntent с помощью статического метода getBroadcast . Для инкапсулированного Intent мы создаем IntentFilter и используем метод registerReceiver, чтобы связать пользовательский BroadcastReceiver с конкретным фильтром намерений. Обратите внимание, что эта привязка может быть альтернативно достигнута декларативным способом с использованием файла манифеста.

Теперь давайте посмотрим, как мы обрабатываем изменения местоположения пользователя. В реализованном методе класса «MyLocationListener» мы извлекаем сохраненную информацию о местоположении («retrievelocationFromPreferences») из класса SharedPreferences . Затем мы используем метод distanceTo для вычисления расстояния между двумя точками, текущей и той, которая соответствует интересующей точке. Это сделано для целей отладки, чтобы мы знали, действительно ли мы вошли в область вокруг точки.

Последний шаг — обработать события входа в зону интереса. Это делается внутри класса «ProximityIntentReceiver», который расширяет BroadcastReceiver и отвечает на пользовательское намерение, которое мы прикрепили к диспетчеру местоположений при добавлении оповещения о близости. Обработка происходит внутри метода onReceive , который вызывается после события. Внутри этого мы извлекаем значение ключа KEY_PROXIMITY_ENTERING из связанного намерения, которое указывает, входит ли оповещение о близости (true) или выходит (false). Код следующий:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.javacodegeeks.android.lbs;
 
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.location.LocationManager;
import android.util.Log;
 
public class ProximityIntentReceiver extends BroadcastReceiver {
     
    private static final int NOTIFICATION_ID = 1000;
 
    @Override
    public void onReceive(Context context, Intent intent) {
         
        String key = LocationManager.KEY_PROXIMITY_ENTERING;
 
        Boolean entering = intent.getBooleanExtra(key, false);
         
        if (entering) {
            Log.d(getClass().getSimpleName(), "entering");
        }
        else {
            Log.d(getClass().getSimpleName(), "exiting");
        }
         
        NotificationManager notificationManager =
            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, null, 0);       
         
        Notification notification = createNotification();
        notification.setLatestEventInfo(context,
            "Proximity Alert!", "You are near your point of interest.", pendingIntent);
         
        notificationManager.notify(NOTIFICATION_ID, notification);
         
    }
     
    private Notification createNotification() {
        Notification notification = new Notification();
         
        notification.icon = R.drawable.ic_menu_notifications;
        notification.when = System.currentTimeMillis();
         
        notification.flags |= Notification.FLAG_AUTO_CANCEL;
        notification.flags |= Notification.FLAG_SHOW_LIGHTS;
         
        notification.defaults |= Notification.DEFAULT_VIBRATE;
        notification.defaults |= Notification.DEFAULT_LIGHTS;
         
        notification.ledARGB = Color.WHITE;
        notification.ledOnMS = 1500;
        notification.ledOffMS = 1500;
         
        return notification;
    }
     
}

Код довольно прост. После того, как мы определим, есть ли у нас входящее или выходящее оповещение о близости, мы готовы предоставить пользовательское уведомление. Для этого мы сначала берем ссылку на соответствующий сервис, т.е. NotificationManager . С помощью этого сервиса мы можем отправлять оповещения пользователю, обернутые вокруг объектов уведомлений . Уведомления могут быть настроены по желанию и могут включать вибрацию , мигающие огни и т. Д. Мы также добавили определенный значок, который появится в строке состояния. SetLatestEventInfo является предпочтительным, когда мы просто хотим добавить основной заголовок и текстовое сообщение. Вы можете найти больше об уведомлениях здесь . Кроме того, мы можем использовать PendingIntent для определения действия, которое будет вызываться, когда пользователь подтверждает уведомление, щелкая по нему. Однако, для простоты, я не использую намерение быть запущенным в моем примере.

Наконец, давайте посмотрим, как выглядит файл манифеста Android:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
 
      package="com.javacodegeeks.android.lbs"
      android:versionCode="1"
      android:versionName="1.0">
       
    <application android:icon="@drawable/icon" android:label="@string/app_name">
         
        <activity android:name=".ProxAlertActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>    
 
    </application>
     
    <uses-sdk android:minSdkVersion="3" />
    
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.VIBRATE" />
 
</manifest>

Здесь ничего особенного, просто не забудьте добавить необходимые разрешения, т.е.

Теперь мы готовы протестировать наше приложение. Запустите конфигурацию Eclipse. Затем перейдите в представление DDMS Eclipse и найдите вкладку «Управление эмуляцией ». Там, помимо прочего, вы найдете раздел «Управление местоположением», который может отправлять данные эмуляции местоположения в эмулятор. На вкладке «Вручную» просто нажмите кнопку «Отправить», там уже установлены некоторые координаты.

После этого у поставщика GPS будет последнее известное местоположение, которое может предоставить по запросу. Итак, нажмите кнопку «Найти координаты», и вот что вы получите:

Затем нажмите «Сохранить точку», чтобы объявить текущее местоположение точкой интереса. Координаты местоположения будут сохранены в настройках, а оповещение о близости будет добавлено в диспетчер местоположений.

Далее, давайте смоделируем тот факт, что пользователь покидает местоположение. Измените значение координат, например, измените широту следующим образом: 37.422006? 37,522006. Мы сейчас довольно далеко от точки интереса. Давайте теперь предположим, что мы приближаемся к нему, поэтому изменим широту на более близкое значение, например: 37.522006? 37,423006.

Это внутри радиуса точки (это было установлено на 1000 метров), таким образом, оповещение сработало, и наш получатель получил уведомление. Там мы создаем наше уведомление и отправляем его через менеджер уведомлений. Уведомление появляется в строке состояния следующим образом:

Вот и все, краткое руководство о том, как реализовать оповещения о близости с платформой Android. Вы можете найти здесь проект Eclipse, созданный для нужд этого руководства.

Статьи по Теме :