пятница, 4 июля 2014 г.

Получаем список датчиков и информацию о них


В этой заметке узнаем, как можно получить список установленных в устройстве датчиков(Sensors) и полную информацию о них. Сразу скажу, что метод работает только на Android устройствах. Я хотел использовать «кроссплатформенный» модуль «System.Sensors», но оказалось, что он не может решить данную задачу на Android, об этом я ещё немного напишу ниже.
И вопрос к разработчикам Embarcadero.

Написано на Delphi XE6 и Android 4.1.2.
Upd(2.10.14). Проверено на Delphi XE7 и Android 4.4.2






Для работы с датчиками в Android API есть класс «SensorManager(android.hardware.SensorManager)», он умеет много всего, но нам сегодня понадобится только один метод «getSensorList(int type)».
Метод возвращает список доступных датчиков указанного типа. 

Типы (для Android API10), также можете найти их тут (Sensor(android.hardware.Sensor)):
  • TYPE_ALL: 'Все типы'
  • TYPE_ACCELEROMETER: 'Акселерометр'
  • TYPE_GRAVITY: 'Датчик силы тяжести'
  • TYPE_GYROSCOPE: 'Гироскоп'
  • TYPE_LIGHT: 'Датчик света'
  • TYPE_LINEAR_ACCELERATION: 'Датчик ускорения'
  • TYPE_MAGNETIC_FIELD: 'Датчик магнитного поля'
  • TYPE_ORIENTATION: 'Датчик ориентации'
  • TYPE_PRESSURE: 'Датчик давления'
  • TYPE_PROXIMITY: 'Датчик приближения'
  • TYPE_ROTATION_VECTOR: 'Датчик вращения'
  • TYPE_TEMPERATURE: 'Датчик температуры'
Для получения списка датчиков у нас всё готово. Теперь нам необходимо вывести всю доступную техническую информацию о датчиках из списка, для этого воспользуемся классом «Sensor(android.hardware.Sensor)», который содержит ещё и методы.

Методы (для Android API10):
  • getMaximumRange() – Максимальное значение датчика
  • getMinDelay() – Минимально допустимая задержка между двумя событиями (в микросекундах)
  • getName() – Имя(название) датчика
  • getPower() – Потребляемая мощность (в mA)
  • getResolution() – Разрешающая способность датчика
  • getType() – Тип датчика (число)
  • getVendor() – Производитель датчика
  • getVersion() – Версия датчика
Как видите информации достаточно, чтобы идентифицировать любой установленный на устройстве датчик.
Теперь напишем простейший код для вывода списка датчиков со всей информацией.

Примечание: Т.к. тип датчика возвращается в виде числа, то выглядит это совсем не информативно, я использовал оператор выбора для приведения типа к более информативному виду.

Код:
uses
  android.hardware.SensorManager, android.hardware.Sensor,
  Androidapi.JNI.JavaTypes, FMX.Helpers.android, Androidapi.JNIBridge,
  Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers;

procedure TForm1.Button1Click(Sender: TObject);
var
  SensorsManagerObj: JObject;
  SensorsManager: JSensorManager;
  Sensors: JList;
  Sensor: JSensor;
  i: Integer;
  StringType: String;
begin
  Memo1.Lines.Clear;

  SensorsManagerObj := SharedActivityContext.getSystemService
    (TJContext.JavaClass.SENSOR_SERVICE);
  SensorsManager := TJSensorManager.Wrap((SensorsManagerObj as ILocalObject)
    .GetObjectID);

  Sensors := SensorsManager.getSensorList(TJSensor.JavaClass.TYPE_ALL);

  Label1.Text := 'Found: ' + IntToStr(Sensors.size) + ' sensors';

  for i := 0 to Sensors.size - 1 do
  begin
    Sensor := TJSensor.Wrap((Sensors.get(i) as ILocalObject).GetObjectID);

    Memo1.Lines.Add('Name: ' + JStringToString(Sensor.getName));

    case Sensor.getType of
      TJSensorTYPE_ACCELEROMETER:
        StringType := 'Акселерометр';
      TJSensorTYPE_GRAVITY:
        StringType := 'Датчик силы тяжести';
      TJSensorTYPE_GYROSCOPE:
        StringType := 'Гироскоп';
      TJSensorTYPE_LIGHT:
        StringType := 'Датчик света';
      TJSensorTYPE_LINEAR_ACCELERATION:
        StringType := 'Датчик ускорения';
      TJSensorTYPE_MAGNETIC_FIELD:
        StringType := 'Датчик магнитного поля';
      TJSensorTYPE_ORIENTATION:
        StringType := 'Датчик ориентации';
      TJSensorTYPE_PRESSURE:
        StringType := 'Датчик давления';
      TJSensorTYPE_PROXIMITY:
        StringType := 'Датчик приближения';
      TJSensorTYPE_ROTATION_VECTOR:
        StringType := 'Датчик вращения';
      TJSensorTYPE_TEMPERATURE:
        StringType := 'Датчик температуры';
    else
      StringType := 'Undefined';
    end;

    Memo1.Lines.Add('Type: ' + StringType);
    Memo1.Lines.Add('Vendor: ' + JStringToString(Sensor.getVendor));
    Memo1.Lines.Add('Version: ' + IntToStr(Sensor.getVersion));
    Memo1.Lines.Add('Resolution: ' + FloatToStr(Sensor.getResolution));
    Memo1.Lines.Add('Max Range: ' + FloatToStr(Sensor.getMaximumRange));
    Memo1.Lines.Add('Power: ' + FloatToStr(Sensor.getPower) + 'mA');
    Memo1.Lines.Add('MinDelay: ' + IntToStr(Sensor.getMinDelay));
    Memo1.Lines.Add('----------------');

  end;
end;

Результат:




Теперь про «System.Sensors».
После того как я написал код для Android API вспомнил, что в Delphi есть «кроссплатформенный» модуль для работы с всевозможными датчиками и решил (основная причина «кроссплатформенность») написать для вас второе решение. Написал код, запустил на устройстве и «увидел фигу», ни какой информации, кроме «Категории (типа)» и «Состояния датчика» у меня не получилось достать.

Код:
uses
  System.Sensors, System.TypInfo;

procedure TForm1.Button2Click(Sender: TObject);
var
  SensorsManager: TSensorManager;
  i: Integer;
  Sensor: TCustomSensor;
begin
  SensorsManager := TSensorManager.Current;
  if SensorsManager.CanActivate then
  begin
    SensorsManager.Activate;

    Memo2.Lines.Clear;
    Label2.Text := 'Found: ' + IntToStr(SensorsManager.Count) + ' sensors';

    for i := 0 to SensorsManager.Count - 1 do
    begin
      Sensor := SensorsManager.Sensors[i];
      Memo2.Lines.Add('Name: ' + Sensor.Name);
      Memo2.Lines.Add('Category: ' + GetEnumName(TypeInfo(TSensorCategory),
        Ord(Sensor.Category)));
      Memo2.Lines.Add('Manufacturer: ' + Sensor.Manufacturer);
      Memo2.Lines.Add('Model: ' + Sensor.Model);
      Memo2.Lines.Add('State: ' + GetEnumName(TypeInfo(TSensorState),
        Ord(Sensor.State)));
      Memo2.Lines.Add('SerialNo: ' + Sensor.SerialNo);
      Memo2.Lines.Add('Description: ' + Sensor.Description);
      Memo2.Lines.Add('UniqueID: ' + Sensor.UniqueID);
      Memo2.Lines.Add('----------------');
    end;
  end;
  SensorsManager.Current.Deactivate;
end;

Результат:


Вопрос к разработчикам Embarcadero: Вы планируете «допилить» этот модуль до кроссплатформенного состояния?

На этом всё. Спасибо за внимание!

Исходный код: Скачать с Google Drive

9 комментариев:

  1. Delphi XE8 ругается на отсутствие android.hardware.SensorListener и android.hardware.SensorEventListener (в android.hardware.SensorManager.pas). С ходу мне не удалось найти где они сейчас находятся, не подскажите где искать? Заранее благодарю!

    ОтветитьУдалить
  2. C android.hardware.SensorListener и android.hardware.SensorEventListener разобрался ;-) А подскажите, как получить показания датчиков?

    ОтветитьУдалить
    Ответы
    1. Данные с датчиков постоянно обновляются, у каждого датчика свой таймер. Для постоянного получения свежих данных, необходимо зарегистрировать BroadcastReceiver, который будет срабатывать при обновлении данных. Это примерная логика, т.к. сам ещё не писал такого, возможны подводные камни.

      Удалить
    2. Вроде не BroadcastReceiver, не нашел возможность подписываться на события от сенсоров. Но нашел что надо использовать SensorEventListener. Вот код, но естественно не работает:
      procedure TForm1.Button1Click(Sender: TObject);
      var
      SensorsManagerObj: JObject;
      SensorsManager: JSensorManager;
      Sensors: JList;
      Sensor: JSensor;
      // SensorListener : JSensorListener;
      SensorEventListener : JSensorEventListener;
      begin
      SensorsManagerObj := SharedActivityContext.getSystemService(TJContext.JavaClass.SENSOR_SERVICE);
      SensorsManager := TJSensorManager.Wrap((SensorsManagerObj as ILocalObject).GetObjectID);
      Sensors := SensorsManager.getSensorList(TJSensor.JavaClass.TYPE_LIGHT);
      Sensor := TJSensor.Wrap((Sensors.get(0) as ILocalObject).GetObjectID);
      SensorsManager.registerListener(SensorEventListener,Sensor,TJSensorManagerSENSOR_DELAY_UI);

      Не уверен что код правильный в плане SensorEventListener, его как то надо создать?
      Далее надо отслеживать события по SensorEventListener.onSensorChanged, но не пойму как в паскале в этой процедуре что то привязать.
      Очень надеюсь на вашу помощь.

      Удалить
    3. Этот комментарий был удален автором.

      Удалить
  3. Подскажите как быть с android.hardware.SensorListener и android.hardware.SensorEventListener, не могу разобраться. Delphi XE7

    ОтветитьУдалить
  4. Плиз, помогите с android.hardware.SensorListener и android.hardware.SensorEventListener, уже всё перерыл - не выходит. Delphi XE7

    ОтветитьУдалить
  5. Помогите получить доступ к функциям в Androidapi.JNI.Bluetooth.pas

    ОтветитьУдалить