Статьи

Использование сенсоров Android в вашем приложении

Приложения для мониторинга приобретают все большую популярность: от родителей, желающих отслеживать местонахождение своих детей и активности в Интернете, до супругов и работодателей, желающих контролировать своих соответствующих супругов и сотрудников. В этой статье мы будем отклоняться от моральных и этических вопросов и сосредоточимся на технических аспектах. Давайте возьмем датчик Android в качестве примера.

Датчик Android возвращает значения, которые можно разделить на три основные категории: перемещение, местоположение и окружение.

Акселерометр — это устройство, которое измеряет правильное ускорение (или «g-force») и обычно используется для обнаружения движения (тряска и наклон). В мобильном мониторинге акселерометр может использоваться для мониторинга пожилых людей, которые живут одни. С помощью акселерометра отслеживается скорость падения, чтобы определить, упал ли человек. Если падение обнаружено, смотритель получает сообщение с предупреждением.

Согласно Дэну Морриллу (менеджеру по совместимости Android и программам с открытым исходным кодом в Google), для правильного использования API необходимо соблюдать следующие три правила:

  • Система координат датчика аналогична системе координат OpenGL и никогда не изменяется, если используется с тем же API для естественной ориентации.
  • Естественная ориентация не всегда портретная.
  • android.view.Display.getRotation()

Посмотрим как это работает

Мы будем использовать SensorEventListenerSensorManager Поскольку мы собираемся использовать жест встряхивания, рекомендуется заблокировать ориентацию устройства.

Создайте новый проект в вашей среде IDE и добавьте в свой файл AndroidManifest.xml

 android:screenOrientation="portrait"

Замените содержимое MainActivity.java

 public class MainActivity extends Activity implements SensorEventListener {

   
    private final String TAG = MainActivity.class.getSimpleName();
    private SensorManager mSensorManager;
    private Sensor mAccelerometer;
    private long lastTime = 0;
    private float lastX, lastY, lastZ;
    private static final int THRESHOLD = 600; //used to see whether a shake gesture has been detected or not.
    TextView coordinates;
    TextView address;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
        Button update = (Button) findViewById(R.id.update_button);
        update.setOnClickListener(new UpdateLocationClick());
        coordinates = (TextView) findViewById(R.id.location_points);
        address = (TextView) findViewById(R.id.location_address);
        if (SApplication.LOCATION != null) {
            double lat = SApplication.LOCATION.getLatitude();
            double lon = SApplication.LOCATION.getLongitude();
            coordinates.setText(lat + " " + lon);
            Geocoder geocoder = new Geocoder(getApplicationContext(), new Locale("en"));
            try {
                List<Address> addresses = geocoder.getFromLocation(lat, lon, 1);
                if (addresses != null && addresses.size() != 0) {
                    StringBuilder builder = new StringBuilder();
                    Address returnAddress = addresses.get(0);
                    for (int i = 0; i < returnAddress.getMaxAddressLineIndex(); i++) {
                        builder.append(returnAddress.getAddressLine(i));
                        builder.append(" ");
                    }
                    address.setText(builder);
                    address.setVisibility(View.VISIBLE);
                } else {
                    Log.e(TAG, "Addresses null");
                }
            } catch (IOException e) {
                Log.e(TAG, "Geocoder exception " + e);
            }
        } else {
            coordinates.setText("No location yet");
            address.setVisibility(View.INVISIBLE);
        }
    }
}

Датчики системы чувствительны. Когда вы держите устройство, оно постоянно находится в движении, независимо от того, насколько устойчива ваша рука. Нам не нужно собирать все эти данные. Мы сохраняем текущее время системы (в миллисекундах) и проверяем, прошло ли более 100 миллисекунд с момента последнего onSensorChanged

Добавьте это в MainActivity.javaMainActivity

 @Override
    public void onSensorChanged(SensorEvent event) {
        Sensor sensor = event.sensor;
        if (sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            float x = event.values[0];
            float y = event.values[1];
            float z = event.values[2];
            long currentTime = System.currentTimeMillis();
            if ((currentTime - lastTime) > 100) {
                long diffTime = (currentTime - lastTime);
                lastTime = currentTime;
                float speed = Math.abs(x + y + z - lastX - lastY - lastZ)/ diffTime * 10000;
                if (speed > THRESHOLD) {
                    getRandomNumber();
                }
                lastX = x;
                lastY = y;
                lastZ = z;
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }

Рекомендуется отменить регистрацию датчика, когда приложение находится в спящем режиме, чтобы сэкономить заряд батареи. Снова добавьте эти функции внутри класса MainActivity

 protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
    }

    protected void onResume() {
        super.onResume();
        mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
    }

    private void getRandomNumber() {
        Random randNumber = new Random();
        int iNumber = randNumber.nextInt(100);
        TextView text = (TextView)findViewById(R.id.number);
        text.setText("" + iNumber);
        RelativeLayout ball = (RelativeLayout) findViewById(R.id.ball);
        Animation a = AnimationUtils.loadAnimation(this, R.anim.move_down_ball_first);
        ball.setVisibility(View.INVISIBLE);
        ball.setVisibility(View.VISIBLE);
        ball.clearAnimation();
        ball.startAnimation(a);
    }

    public class UpdateLocationClick implements View.OnClickListener {

        @Override
        public void onClick(View v) {
            if (SApplication.LOCATION != null) {
                double lat = SApplication.LOCATION.getLatitude();
                double lon = SApplication.LOCATION.getLongitude();
                coordinates.setText(lat + " " + lon);
                Geocoder geocoder = new Geocoder(getApplicationContext(), new Locale("en"));
                try {
                    // get address from location
                    List<Address> addresses = geocoder.getFromLocation(lat, lon, 1);
                    if (addresses != null && addresses.size() != 0) {
                        StringBuilder builder = new StringBuilder();
                        Address returnAddress = addresses.get(0);
                        for (int i = 0; i < returnAddress.getMaxAddressLineIndex(); i++) {
                            builder.append(returnAddress.getAddressLine(i));
                            builder.append(" ");
                        }
                        address.setText(builder);
                        address.setVisibility(View.VISIBLE);
                    } else {
                        Log.e(TAG, "Addresses null");
                    }
                } catch (IOException e) {
                    Log.e(TAG, "Geocoder exception " + e);
                }
            } else {
                Toast.makeText(getApplicationContext(), "Check GPS status and internet connection", Toast.LENGTH_LONG).show();
                coordinates.setText("No location yet");
                address.setVisibility(View.INVISIBLE);
            }
        }

    }

Значение датчика Android позволяет в реальном времени определять местоположение пользователя GPS на основных плоскостях. Эти данные отправляются человеку, который имеет доступ к приложению для мониторинга, в любое время, когда есть соединение Wi-Fi или сеть 3G / 4G.

В отличие от акселерометра, автономные устройства GPS зависят от индикаторов спутникового радио. В мобильном мониторинге AGPS (вспомогательная система глобального позиционирования) выбирается вместо GPS. Это связано с тем, что эти вышки сети оснащены приемниками GPS для увеличения скорости сигнала. Приемники получают информацию со спутников, а затем отправляют ее на мобильные телефоны со всеми необходимыми вычислениями. В результате вы получите:

  • Быстрая локация
  • Меньшее использование батареи
  • Приобретение в помещении

Сначала мы добавим некоторые разрешения в файл AndroidManifest.xml

 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />

Мы будем использовать фоновый сервис для обнаружения нового местоположения. Создайте новый файл класса с именем LocationService.java

 public class LocationService extends Service {
    private static final String TAG = LocationService.class.getSimpleName();
    private LocationManager mLocationManager = null;
    private static final int INTERVAL = 1000; // minimum time interval between location updates (milliseconds)
    private static final float DISTANCE = 10f; // minimum distance between location updates (meters)

    private class LocationListener implements android.location.LocationListener {
        Location mLastLocation;

        public LocationListener(String provider) {
            Log.e(TAG, "LocationListener " + provider);
            mLastLocation = new Location(provider);
        }

        @Override
        public void onLocationChanged(Location location) {
            mLastLocation.set(location);
            SApplication.LOCATION = location;
        }

        @Override
        public void onProviderDisabled(String provider) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

    }

    LocationListener[] mLocationListeners = new LocationListener[]{
            new LocationListener(LocationManager.GPS_PROVIDER),
            new LocationListener(LocationManager.NETWORK_PROVIDER)
    };

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }
}

Теперь мы запрашиваем обновления местоположения у обоих провайдеров, GPS и сети. Добавьте следующее в класс LocationService

 @Override
    public void onCreate()
    {
        if (mLocationManager == null) {
            mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        }try {
        mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, INTERVAL, DISTANCE, mLocationListeners[1]);
    } catch (java.lang.SecurityException ex) {
        Log.i(TAG, "fail to request location update, ignore", ex);
    } catch (IllegalArgumentException ex) {
        Log.d(TAG, "network provider does not exist, " + ex.getMessage());
    }
        try {
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, INTERVAL, DISTANCE, mLocationListeners[0]);
        } catch (java.lang.SecurityException ex) {
            Log.i(TAG, "fail to request location update, ignore", ex);
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "gps provider does not exist " + ex.getMessage());
        }
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                try {
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                } catch (Exception ex) {
                    Log.i(TAG, "fail to remove location listeners, ignore", ex);
                }
            }
        }
    }

Теперь мы определяем наш класс Application, создаем новый класс с именем SApplication

 public class SApplication extends Application {
	private final String TAG = SApplication.class.getSimpleName();
 	public static Location LOCATION = null;</p>

	public void onCreate() {
    	Intent location = new Intent(getApplicationContext(), LocationService.class);
    	startService(location);
	}
}

Чтобы завершить приложение, требуется много элементов пользовательского интерфейса, посмотрите в репозитории на GitHub, чтобы увидеть их все и импортировать в свой собственный проект. Для быстрого достижения этого вам может быть проще заменить часть содержимого папки res на папку res в демонстрационном проекте.

Чтобы протестировать приложение, вам нужно будет запустить его на реальном устройстве, а не в эмуляторе. Встряхните телефон, чтобы увидеть значения акселерометра и коснитесь
обновить кнопку, чтобы получить ваше местоположение. Если вы не можете заставить местоположение работать, попробуйте включить режим «высокой точности».

Это был простой пример, иллюстрирующий потенциал некоторых сенсоров Android, надеюсь, вы сможете увидеть потенциальные возможности использования в реальных приложениях.