уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

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

В представленном ниже примере рассматривается вопрос о том, как в Delphi получить информацию обо всех установленных датчиках в мобильном устройстве.

Основная информация по компоненту
Исходник официального примера и документация SourceForge DocWiki

На главной форме демонстрационного приложения расположены следующие компоненты:

  • TListBox — для вывода информации о датчиках
  • TTimer — при срабатывании таймера выводится информация с текущего датчика устройства
  • TLabel
  • TButton — кнопка возврата к выбору датчика из списка

Внешний вид демонстрационного приложения представлен на рисунке ниже:

Демонстрационный пример

Демонстрационное приложение выводит список датчиков, установленных в мобильном устройстве и, при выборе определенного датчика, показывает его свойства.

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

uses
  System.Permissions,
{$IFDEF ANDROID}
  Androidapi.JNI.Os,
  Androidapi.JNI.JavaTypes,
  Androidapi.Helpers,
{$ENDIF}
  FMX.DialogService;
Про условную компиляцию в Delphi читайте эту статью

В секции private формы приложения определены следующие поля:

type
  TfrmAboutSensors = class(TForm)
    [...]
  private
    { Private declarations }
    FShowInfo: Boolean; //True, если выбран какой-либо датчик в списке
    FActiveSensor: TCustomSensor; //содержит объект выбранного датчика
    [...]

Обработчик события OnCreate главной формы:

var
  LSensorCat: TSensorCategory;
begin
  FActiveSensor := nil;
  FShowInfo := False;
  ReAlignComponents; //выравниваем компоненты на форме
  {получаем доступ к общему менеджеру датчиков}
  TSensorManager.Current.Activate(); 
  {перебираем все возможные категории датчиков и формируем их список}
  for LSensorCat in AllCat do
    CreateIfExists(LSensorCat);
end;

TSensorManager используется для идентификации датчиков, подключенных к устройству и предоставления их для использования в приложениях. В приведенном выше коде AllCat представляет собой константу, описанную в модуле приложения:

const
  AllCat: TSensorCategories =
  [TSensorCategory.Location, TSensorCategory.Environmental, TSensorCategory.Motion,
  TSensorCategory.Orientation, TSensorCategory.Mechanical, TSensorCategory.Electrical,
  TSensorCategory.Biometric, TSensorCategory.Light, TSensorCategory.Scanner];

Метод CreateIfExists заполняет список TListBox информацией о датчиках выбранной категории и выглядит следующим образом:

procedure TfrmAboutSensors.CreateIfExists(ASensorCategory: TSensorCategory);
var
  LSensorArray: TSensorArray;
  LSensor: TCustomSensor;
  LHeader: TListBoxGroupHeader;
  LItem: TListBoxItem;
begin
  //получаем массив датчиков устройства, относящихся к заданной категории
  LSensorArray := TSensorManager.Current.GetSensorsByCategory(ASensorCategory);
  //создаем элемент списка - заголовок (TListBoxGroupHeader)
  LHeader := TListBoxGroupHeader.Create(Owner);
  LHeader.Parent := lbMain;
  LHeader.Text := GetSensorCategoryName(ASensorCategory); //метод представлен ниже
  LHeader.Height := LHeader.Height * 2;
  //проходим по каждому датчику в массиве и создаем для него отдельную строку списка
  for LSensor in LSensorArray do
  begin
    LItem := TListBoxItem.Create(Owner);
    LItem.Parent := lbMain;
    LItem.Text := GetSensorType(LSensor); //определяем тип датчика
    LItem.ItemData.Accessory := TListBoxItemData.TAccessory.aDetail;
    LItem.Data := LSensor;
    LItem.OnClick := ListBoxItemClick;
    LItem.Height := LItem.Height * 2;
    LItem.Font.Size := LItem.Font.Size * 2;
  end;
end;
 
function TfrmAboutSensors.GetSensorCategoryName(ASensorCategory: TSensorCategory): string;
begin
  Result := cND;
  case ASensorCategory of
    TSensorCategory.Location: Result := 'Location';
    TSensorCategory.Environmental: Result := 'Environmental';
    TSensorCategory.Motion: Result := 'Motion';
    TSensorCategory.Orientation: Result := 'Orientation';
    TSensorCategory.Mechanical: Result := 'Mechanical';
    TSensorCategory.Electrical: Result := 'Electrical';
    TSensorCategory.Biometric: Result := 'Biometric';
    TSensorCategory.Light: Result := 'Light';
    TSensorCategory.Scanner: Result := 'Scanner';
  end;
end;

Для определения типа датчика используется следующий метод:

function TfrmAboutSensors.GetSensorType(ASensor: TCustomSensor): string;
begin
  Result := cND;
  case ASensor.Category of
    TSensorCategory.Location: Result := GetTypeNameLocation(TCustomLocationSensor(ASensor).SensorType);
    TSensorCategory.Environmental:  Result := GetTypeNameEnv(TCustomEnvironmentalSensor(ASensor).SensorType);
    TSensorCategory.Motion: Result := GetTypeNameMotion(TCustomMotionSensor(ASensor).SensorType);
    TSensorCategory.Orientation: Result := GetTypeNameOrientation(TCustomOrientationSensor(ASensor).SensorType);
    TSensorCategory.Mechanical: Result := GetTypeNameMech(TCustomMechanicalSensor(ASensor).SensorType);
    TSensorCategory.Electrical: Result := GetTypeNameElectro(TCustomElectricalSensor(ASensor).SensorType);
    TSensorCategory.Biometric: Result := GetTypeNameBio(TCustomBiometricSensor(ASensor).SensorType);
    TSensorCategory.Light: Result := GetTypeNameLight(TCustomLightSensor(ASensor).SensorType);
    TSensorCategory.Scanner:  Result := GetTypeNameScanner(TCustomScannerSensor(ASensor).SensorType);
  end;
end;

В этом методе в зависимости от категории к которой относится датчик выполняется одна из функций: GetTypeNameLocation, GetTypeNameEnv, GetTypeNameMotion, GetTypeNameOrientation и так далее, которые служат для вывода информации и типе датчик в человекопонятной форме. Все эти методы выглядят идентично, например, так выглядит метод определения типа датчика движения:

function TfrmAboutSensors.GetTypeNameMotion(AType: TMotionSensorType): string;
begin
  case AType of
    TMotionSensorType.Accelerometer1D: Result := 'Accelerometer1D';
    TMotionSensorType.Accelerometer2D: Result := 'Accelerometer2D';
    TMotionSensorType.Accelerometer3D: Result := 'Accelerometer3D';
    TMotionSensorType.MotionDetector: Result := 'MotionDetector';
    TMotionSensorType.Gyrometer1D: Result := 'Gyrometer1D';
    TMotionSensorType.Gyrometer2D: Result := 'Gyrometer2D';
    TMotionSensorType.Gyrometer3D: Result := 'Gyrometer3D';
    TMotionSensorType.Speedometer: Result := 'Speedometer';
    TMotionSensorType.LinearAccelerometer3D: Result := 'LinearAccelerometer3D';
    TMotionSensorType.GravityAccelerometer3D: Result := 'GravityAccelerometer3D';
  else
    Result := cND;
  end;
end;

После того, как список всех датчиков устройства создан, можно кликнуть по названию датчика в списке и приложение покажет по этому датчику подробную информацию. Обработчик OnClick элемента списка выглядит следующим образом:

procedure TfrmAboutSensors.ListBoxItemClick(Sender: TObject);
begin
  if Sender is TListBoxItem then
  begin
    FActiveSensor := TCustomSensor(TListBoxItem(Sender).Data);
    if (FActiveSensor <> nil) and (not FActiveSensor.Started) then
    begin
{$IFDEF ANDROID}
      if FActiveSensor.Category = TSensorCategory.Location then
      begin
        PermissionsService.RequestPermissions([JStringToString(TJManifest_permission.JavaClass.ACCESS_FINE_LOCATION)],
          procedure(const APermissions: TArray; const AGrantResults: TArray)
          begin
            if (Length(AGrantResults) = 1) and (AGrantResults[0] = TPermissionStatus.Granted) then
              FActiveSensor.Start
            else
              TDialogService.ShowMessage('Location permission not granted');
          end)
      end;
{$ELSE}
      FActiveSensor.Start;
{$ENDIF}
    end;
  end;
  FShowInfo := True;
end;

Здесь опять же использована условная компиляция в Delphi для того, чтобы в случае, если приложение запущено под Android и выбран датчик местоположения, то можно было запросить разрешение на использование этого датчика. Если выбран датчик, то он запускается и пользователь видит информацию полученную с него. Для этого, в обработчике таймера OnTimer используется следующий код:

procedure TfrmAboutSensors.Timer1Timer(Sender: TObject);
var
  ResultText: string;
  LStep: Single;
begin
{
  SysDebug(
    'Assigned(FActiveSensor) = ' + BoolToStr(Assigned(FActiveSensor))
    + '| FShowInfo = ' + BoolToStr(FShowInfo)
  );
}
  if Assigned(FActiveSensor) then
  begin
    case FActiveSensor.Category of
      TSensorCategory.Location: ResultText := GetInfoAboutLocation(FActiveSensor);
      TSensorCategory.Environmental: ResultText := GetInfoAboutEnv(FActiveSensor);
      TSensorCategory.Motion: ResultText := GetInfoAboutMotion(FActiveSensor);
      TSensorCategory.Orientation: ResultText := GetInfoAboutOrientation(FActiveSensor);
      TSensorCategory.Mechanical: ResultText := GetInfoAboutMechanical(FActiveSensor);
      TSensorCategory.Electrical: ResultText := GetInfoAboutElectro(FActiveSensor);
      TSensorCategory.Biometric: ResultText := GetInfoAboutBiometric(FActiveSensor);
      TSensorCategory.Light: ResultText := GetInfoAboutLight(FActiveSensor);
      TSensorCategory.Scanner: ResultText := GetInfoAboutScanner(FActiveSensor);
    end;
    lInfo.Text := ResultText;
  end;
  if not FOnOneScreen then
  begin
    if FShowInfo then
    begin
      if lInfo.Position.Point.X > (cBorder*2) then
      begin
        LStep := Width / 5;
        lInfo.Position.Point := PointF(lInfo.Position.Point.X - LStep, cBorder);
        lbMain.Position.Point := PointF(lbMain.Position.Point.X - LStep, cBorder);
      end;
    end
    else
    begin
      if lbMain.Position.Point.X < cBorder then
      begin
        LStep := Width / 5;
        lInfo.Position.Point := PointF(lInfo.Position.Point.X + LStep, cBorder);
        lbMain.Position.Point := PointF(lbMain.Position.Point.X + LStep, cBorder);
      end;
    end;
  end;
end;

Опять же, в зависимости от выбранного датчика используются различные методы: GetInfoAboutLocation, GetInfoAboutEnv, GetInfoAboutMotion, GetInfoAboutLight и так далее. Ниже представлен метод вывода информации в Delphi датчика освещенности в зависимости от свойств самого датчика:

function TfrmAboutSensors.GetInfoAboutLight(ASensor: TCustomSensor): string;
var
  ls: TCustomLightSensor;
  LValues: string;
  LProp: TCustomLightSensor.TProperty;
begin
  LValues := '';
  ls := TCustomLightSensor(ASensor);
  for LProp in ls.AvailableProperties do
  begin
    case LProp of
      TCustomLightSensor.TProperty.Lux:
        LValues := LValues + ToFormStr('Lux', ls.Lux);
      TCustomLightSensor.TProperty.Temperature:
        LValues := LValues + ToFormStr('Temperature', ls.Temperature);
      TCustomLightSensor.TProperty.Chromacity:
        LValues := LValues + ToFormStr('Chromacity', ls.Chromacity);
    end;
  end;
  Result := GetFullInfo(
    GetSensorCategoryName(ASensor.Category),
    GetTypeNameLight(ls.SensorType),
    ls.ClassName,
    LValues
  );
end;

На рисунке ниже представлен пример работающего демонстрационного приложения кратко, что показано:

Таким образом, используя менеджер датчиков (TSensorManager) в Delphi можно получить информацию по всем датчикам мобильного устройства.

При подготовке статьи использовалась информация со следующих ресурсов:

  1. Официальный репозиторий демонстрационных примеров Delphi на SourceForge

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.