Перейти к содержанию
Fire Monkey от А до Я
  • 0

Контролировать приход SMS


Rusland

Вопрос

Рекомендуемые сообщения

  • 0

Хорошо, тогда почитайте тут https://forums.embarcadero.com/message.jspa?messageID=758137

 

Ещё сюда милости прошу(пример на Delphi архивирован, стягивайте с SVN) https://code.google.com/p/delphi-android-broadcast-receiver-component/

 

Вот описание BroadcastReceiver(JAVA) http://developer.android.com/reference/android/content/BroadcastReceiver.html

Изменено пользователем MAD_EVAL
Ссылка на комментарий
  • 0

Ещё сюда милости прошу(пример на Delphi архивирован, стягивайте с SVN) https://code.google.com/p/delphi-android-broadcast-receiver-component/

MAD_EVAL,  я читал про этот готовый компонент, но сам автор признается что не знает как получать через него SMS... 

Может кто разъяснит?

 

 

 

Хорошо, тогда почитайте тут https://forums.embar...essageID=758137

Сегодня только читал я это (не стоит думать что я задаю вопросы предварительно не прошерстив интернет)... в ответе Remy Lebeau дает код, который ни разу не компилируется... да он и сам признается что untested :)

Кто-нибудь может разобрать/допилить его код? :)

Изменено пользователем Rusland
Ссылка на комментарий
  • 0

Есть вот такая процедура (взял со stackoverflow), которая позволяет просматривать все СМС во Входящих:
 

procedure FetchSMS;
var
  cursor: JCursor;
  uri: Jnet_Uri;
  uri_del: Jnet_Uri;
 
  id_smsid: integer;
  id_smssender: integer;
  id_smsbody: integer;
 
  smsid: string;
  smssender: string;
  smsbody: string;
  smsreply: string;
  i:integer;
begin
 
  cursor:= SharedActivity.getContentResolver.query(uri, nil, nil, nil, nil);
 
  id_smsid:= cursor.getColumnIndex(StringToJstring('_id'));
  id_smssender:= cursor.getColumnIndex(StringToJstring('address'));
  id_smsbody:=cursor.getColumnIndex(StringToJstring('body'));
 
  cursor.moveToLast;
  for i:=cursor.getCount downto 1 do
    begin
      smsid:=JStringToString(cursor.getString(id_smsid));
 
      if not IsSMSAnswered(smsid) then
        begin
          smssender:=JStringToString(cursor.getString(id_smssender));
          smsbody:=JStringToString(cursor.getString(id_smsbody));
 
          if smsbody = 'abc' then
          begin
            // делаем что-то
          end;
...
    end;
end;



но ее приходится вызывать в цикле, что не есть хорошо.
Как бы ее связать с BroadcastReceiver? Есть готовый код? 


PS. форум глючит или мой браузер?

Изменено пользователем Rusland
Ссылка на комментарий
  • 0
unit Unit1;

interface

uses
  System.SysUtils,
  System.Types,
  System.UITypes,
  System.Classes,
  System.Variants,

  FMX.Types,
  FMX.Controls,
  FMX.Forms,
  FMX.Graphics,
  FMX.Dialogs,

  CSBroadcastReceiver,
  Androidapi.JNI.JavaTypes.Own,

  Androidapi.Jni,
  AndroidApi.JNI.GraphicsContentViewText,
  Androidapi.Jni.JavaTypes,
  Androidapi.JNI.Os,
  Androidapi.JNIBridge,
  Androidapi.JNI.Telephony, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo,
  FMX.StdCtrls;


type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    procedure CreateBroadcastReceiver;
    procedure BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent);
    procedure CheckSmsInState(Context: JContext; Intent: JIntent);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  BroadcastReceiver: TCSBroadcastReceiver;

implementation

uses
  Androidapi.Jni.App, Androidapi.Helpers, Androidapi.Log;

{$R *.fmx}

procedure TForm1.CreateBroadcastReceiver;
begin
  if not Assigned(BroadcastReceiver) then
    begin
      BroadcastReceiver:= TCSBroadcastReceiver.Create(nil);
      BroadcastReceiver.OnReceive:= BroadcastReceiverOnReceive;
      BroadcastReceiver.RegisterReceive;
      //BroadcastReceiver.Add('android.intent.action.PHONE_STATE');
      //BroadcastReceiver.Add('android.intent.action.NEW_OUTGOING_CALL');
      BroadcastReceiver.Add('android.provider.Telephony.SMS_RECEIVED');
    end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  if Assigned(BroadcastReceiver) then
    BroadcastReceiver.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateBroadcastReceiver;
end;

procedure TForm1.BroadcastReceiverOnReceive(csContext: JContext; csIntent: JIntent);
begin
  //CheckPhoneCallState(csContext, csIntent);
  CheckSmsInState(csContext, csIntent); // тут поставил брейкпоинт
end;

procedure TForm1.CheckSmsInState(Context: JContext; Intent: JIntent);
var
  obj: JObject;
  I: Integer;
  msg : JSmsMessage;
  pdus : TJavaArray<JObject>;
  newPhoneNumber, text : string;
begin
  if SameText(JStringToString(Intent.getAction), 'android.provider.Telephony.SMS_RECEIVED') then
  begin
    Memo1.Lines.Add('SMS Received');
  end;
end;

end.

Переделал чуть программу, которая отбивала входящие звонки. Тут как раз используется CSBroadcastReceiver.pas

unit CSBroadcastReceiver;

interface

uses
  System.Classes
  ,System.SysUtils
  {$IFDEF ANDROID}
  ,Androidapi.JNI.Embarcadero
  ,Androidapi.JNI.GraphicsContentViewText
  ,Androidapi.Helpers
  ,Androidapi.JNIBridge
  ,Androidapi.JNI.JavaTypes
  ,Androidapi.JNI.App
  {$ENDIF}
  ;

type

  {$IFNDEF ANDROID}
  JIntent = class
  end;
  JContext = class
  end;
  {$ENDIF}

  TCSBroadcastReceiver= class;
  TOnReceive = procedure (csContext: JContext; csIntent: JIntent) of object;

  {$IFDEF ANDROID}
  TCSListener = class(TJavaLocal, JFMXBroadcastReceiverListener)
    private
      FOwner: TCSBroadcastReceiver;
    public
      constructor Create(AOwner: TCSBroadcastReceiver);
      procedure OnReceive(csContext: JContext; csIntent: JIntent); cdecl;
  end;
  {$ENDIF}


  TCSBroadcastReceiver = class(TComponent)
    private
      {$IFDEF ANDROID}
      FReceiver: JBroadcastReceiver;
      FListener : TCSListener;
      {$ENDIF}
      FOnReceive: TOnReceive;
      FItems: TStringList;
      function GetItem(const csIndex: Integer): String;

    public
      constructor Create(AOwner: TComponent); override;
      destructor  Destroy; override;
      procedure SendBroadcast(csValue: String);
      procedure Add(csValue: String);
      procedure Delete(csIndex: Integer);
      procedure Clear;
      procedure setResultData(data: JString);
      function Remove(const csValue: String): Integer;
      function First: String;
      function Last: String;
      function HasPermission(const csPermission: string): Boolean;
      procedure RegisterReceive;
      property Item[const csIndex: Integer]: string read GetItem; default;
      property Items: TStringList read FItems write FItems;
    published
      property OnReceive: TOnReceive read FOnReceive write FOnReceive;
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('Classicsoft', [TCSBroadcastReceiver]);
end;

{ TCSBroadcastReceiver }

procedure TCSBroadcastReceiver.setResultData(data: Jstring);
begin
  FReceiver.setResultData(data);
end;

procedure TCSBroadcastReceiver.Add(csValue: String);
{$IFDEF ANDROID}
var
  Filter: JIntentFilter;
{$ENDIF}
begin
  {$IFDEF ANDROID}
  if (FListener = nil) or (FReceiver = nil) then
  begin
    Raise Exception.Create('First use RegisterReceive!');
    Exit;
  end;
  {$ENDIF}

  if FItems <> nil then
    if FItems.IndexOf(csValue) = -1 then
    begin
    {$IFDEF ANDROID}
      filter := TJIntentFilter.Create;
      filter.addAction(StringToJString(csValue));
      TAndroidHelper.Context.registerReceiver(FReceiver, filter);
    {$ENDIF}
      FItems.Add(csValue);
    end;
end;

procedure TCSBroadcastReceiver.Clear;
begin
  FItems.Clear;
end;

constructor TCSBroadcastReceiver.Create(AOwner: TComponent);
begin
  inherited;
  FItems := TStringList.Create;
end;

procedure TCSBroadcastReceiver.Delete(csIndex: Integer);
begin
  if FItems <> nil then
  begin
    FItems.Delete(csIndex);
    {$IFDEF ANDROID}
      TAndroidHelper.Activity.UnregisterReceiver(FReceiver);
      RegisterReceive;
    {$ENDIF}
  end;
end;

destructor TCSBroadcastReceiver.Destroy;
begin
  FItems.Free;
{$IFDEF ANDROID}
  if FReceiver <> nil  then
    TAndroidHelper.Activity.UnregisterReceiver(FReceiver);
{$ENDIF}
  inherited;
end;

function TCSBroadcastReceiver.First: String;
begin
  Result := FItems[0];
end;

function TCSBroadcastReceiver.GetItem(const csIndex: Integer): String;
begin
  Result := FItems[csIndex];
end;

function TCSBroadcastReceiver.HasPermission(const csPermission: string): Boolean;
{$IFDEF ANDROID}
begin
  Result := TAndroidHelper.Activity.checkCallingOrSelfPermission(StringToJString(csPermission)) = TJPackageManager.JavaClass.PERMISSION_GRANTED;
{$ELSE}
begin
  Result := False;
{$ENDIF}
end;

function TCSBroadcastReceiver.Last: String;
begin
  Result := FItems[FItems.Count];
end;

procedure TCSBroadcastReceiver.RegisterReceive;
{$IFDEF ANDROID}
var
  I: Integer;
begin
  if FListener = nil then
    FListener := TCSListener.Create(Self);
  if FReceiver = nil then
    FReceiver := TJFMXBroadcastReceiver.JavaClass.init(FListener);
  if FItems <> nil then
    if FItems.Count > 0 then
      for I := 0 to FItems.Count -1 do
        Add(FItems[I]);
{$ELSE}
begin
{$ENDIF}
end;

function TCSBroadcastReceiver.Remove(const csValue: String): Integer;
begin
  Result := FItems.IndexOf(csValue);
  if Result > -1 then
    FItems.Delete(Result);
end;

procedure TCSBroadcastReceiver.SendBroadcast(csValue: String);
{$IFDEF ANDROID}
var
  Inx: JIntent;
begin
  Inx := TJIntent.Create;
  Inx.setAction(StringToJString(csValue));
  TAndroidHelper.Context.sendBroadcast(Inx);
{$ELSE}
begin
{$ENDIF}
end;

{$IFDEF ANDROID}
constructor TCSListener.Create(AOwner: TCSBroadcastReceiver);
begin
  inherited Create;
  FOwner := AOwner;
end;

procedure TCSListener.OnReceive(csContext: JContext; csIntent: JIntent);
begin
  if Assigned(FOwner.OnReceive) then
    FOwner.onReceive(csContext, csIntent);
end;

{$ENDIF}

end.

Так вот со звонками работает отлично, а при входящих SMS тишина.

Есть идеи?

 

 

Просто надо было разрешить в USER PERMISSION добавить Receive SMS (у меня стояла только галочка READ SMS).

Код рабочий - в момент прихода SMS можно вызывать FetchSMS. Но если кто-нибудь сможет привести в чувство код от Remy Lebeau, при котором при получении сразу же будет читаться пришедшая СМС, будет еще лучше.

Изменено пользователем Rusland
Ссылка на комментарий
  • 0
Цитата

Переделал чуть программу, которая отбивала входящие звонки. Тут как раз используется CSBroadcastReceiver.pas

Rusland, как ты обошел проблему "not allowed to send broadcast android.intent.action.PHONE_STATE" при вызове SendBroadcast?

 

Изменено пользователем Pax Beach
Ссылка на комментарий
  • 0

Снова вернулся к проблеме получения SMS. Вернее отловить ее приход через BroadcastReceiver ("android.provider.Telephony.SMS_RECEIVED") не проблема. А вот как прочитать я так до сих пор и не разобрался.

На Java этот код выглядит так:

	private final String SMS_RECEIVED =
			"android.provider.Telephony.SMS_RECEIVED";

	public SMSReceiver() {
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		if (    intent!=null &&
				intent.getAction()!=null &&
				intent.getAction().compareToIgnoreCase(SMS_RECEIVED)==0){
			SmsMessage[] smss;
			smss = getMessagesFromIntent(intent);
			String from = smss[0].getDisplayOriginatingAddress();
			String body = "";
			for (SmsMessage s: smss){
				body += s.getDisplayMessageBody();
			}
			Toast.makeText(context, from+"\n"+body,
					Toast.LENGTH_LONG).show();
			//Log.i("MSG", from+"\n"+body);
		}
	}

Как сделать на Delphi?

Ссылка на комментарий
  • 0
В 18.06.2016 в 20:02, Pax Beach сказал:

Rusland, как ты обошел проблему "not allowed to send broadcast android.intent.action.PHONE_STATE" при вызове SendBroadcast?

Только сейчас заметил вопрос. О чем речь? )

Ссылка на комментарий
  • 0
5 минут назад, Rusland сказал:

Только сейчас заметил вопрос. О чем речь? )

Ну была проблема при отправке определенных ACTION в broadcast.

Видимо, твоего примера это не касается.

 

На чем ты споткнулся при переводе JAVA примера в Delphi?

Выглядит, вроде, все понятно. Получил сообщение broadcast — значит имеешь контекст и намерение (Intent). Из него получаешь методом getMessagesFromIntent класса TJSms_Intents нужное тебе сообщение.

Изменено пользователем Pax Beach
дополнил ответ
Ссылка на комментарий
  • 0
  • Модераторы

я пользовался таким кодом когда-то, текст в смс был таким 

Цитата

exFood: 0000

// получение кода с СМС отправленного при регистрации
function GetSMSCode: string;
// uses
// FMX.Helpers.Android, Androidapi.JNI.Net, Androidapi.JNI.JavaTypes, Androidapi.JNI.Telephony,
// Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers
{$IFDEF ANDROID}
var
  cursor: JCursor;
  uri: Jnet_Uri;
  address, body, Return: string;
  msgunixtimestampms: int64;
  addressidx, msgdateidx, bodyidx: integer;
  aUTCOffset: integer;
  SMSTime: TDateTime;
begin
  try
    Return := '';
    uri := StrToJURI('content://sms/inbox');
    cursor := SharedActivity.getContentResolver.query(uri, nil, nil, nil, nil);
    addressidx := cursor.getColumnIndex(StringToJstring('address'));
    msgdateidx := cursor.getColumnIndex(StringToJstring('date'));
    bodyidx := cursor.getColumnIndex(StringToJstring('body'));
    cursor.moveToFirst;
    address := JStringToString(cursor.getString(addressidx));
    msgunixtimestampms := cursor.getLong(msgdateidx);
    body := JStringToString(cursor.getString(bodyidx));
    if Pos('exFood:', body) > 0 then // эта строчка всегда приходит в сообщении
    begin
      // учитываем Часовой пояс
      with TTimeZone.Create do
      begin
        aUTCOffset := Local.GetUtcOffset(Now).Hours;
        Free;
      end;
      SMSTime := IncHour(myUnixToDateTime(msgunixtimestampms div 1000), aUTCOffset);
      // вычисляем последнюю смс
      if DayOf(SMSTime) = DayOf(Now) then
        if MinuteOf(SMSTime) = MinuteOf(Now) then 
          Return := Copy(body, body.Length - 3, MAX_PATH); // вырезаем код подтверждения
    end;
    Result := Return;
  except
    Result := 'ERROR';
  end;
{$ELSE}

begin
  Result := '0000';
{$ENDIF}
end;

 

Ссылка на комментарий
  • 0
  • Модераторы
24 минуты назад, Rusland сказал:

smss = getMessagesFromIntent(intent);

этого метода нет в SDK делфи, нужно самому прописать, остальное есть

Ссылка на комментарий
  • 0
8 минут назад, ZuBy сказал:

этого метода нет в SDK делфи, нужно самому прописать, остальное есть

У в Берлине АПД1 в исходниках, Androidapi.JNI.Provider.pas

есть такой метод.

UPD:

А... обратил внимание, что метод появился с API-19.

Изменено пользователем Pax Beach
Ссылка на комментарий
  • 1
  • Модераторы
49 минут назад, Rusland сказал:

Снова вернулся к проблеме получения SMS. Вернее отловить ее приход через BroadcastReceiver ("android.provider.Telephony.SMS_RECEIVED") не проблема. А вот как прочитать я так до сих пор и не разобрался.

На Java этот код выглядит так:


	private final String SMS_RECEIVED =
			"android.provider.Telephony.SMS_RECEIVED";

	public SMSReceiver() {
	}

	@Override
	public void onReceive(Context context, Intent intent) {
		if (    intent!=null &&
				intent.getAction()!=null &&
				intent.getAction().compareToIgnoreCase(SMS_RECEIVED)==0){
			SmsMessage[] smss;
			smss = getMessagesFromIntent(intent);
			String from = smss[0].getDisplayOriginatingAddress();
			String body = "";
			for (SmsMessage s: smss){
				body += s.getDisplayMessageBody();
			}
			Toast.makeText(context, from+"\n"+body,
					Toast.LENGTH_LONG).show();
			//Log.i("MSG", from+"\n"+body);
		}
	}

Как сделать на Delphi?

const
  SMS_RECEIVED = 'android.provider.Telephony.SMS_RECEIVED';

procedure SMSReceiver(aIntent: JIntent);
//uses
//  Androidapi.JNI.JavaTypes, Androidapi.JNI.Telephony, Androidapi.JNI.Provider, Androidapi.JNIBridge,
//  Androidapi.JNI.GraphicsContentViewText, Androidapi.Helpers;
var
  aSmss: TJavaObjectArray<JSmsMessage>;
  aSms: JSmsMessage;
  aFrom: string;
  aBody: string;
  I: Integer;
begin
  if aIntent = nil then
    exit;
  if (aIntent.getAction <> nil) and (aIntent.getAction.compareToIgnoreCase(StringToJString(SMS_RECEIVED)) = 0) then
  begin
    aSmss := TJavaObjectArray<JSmsMessage>.Create;
    aSmss := TJSms_Intents.JavaClass.getMessagesFromIntent(aIntent);
    aFrom := JStringToString(aSmss[0].getDisplayOriginatingAddress);
    aBody := '';
    for I := 0 to aSmss.Length - 1 do
    begin
      aSms := aSmss[I];
      aBody := aBody + JStringToString(aSms.getDisplayMessageBody);
    end;
  end;
end;

не проверял, но по коду так, только как нить вернуть нужно aFrom, aBody

сделать как функцию и тд..

Изменено пользователем ZuBy
Ссылка на комментарий
  • 0
1 час назад, ZuBy сказал:

я пользовался таким кодом когда-то, текст в смс был таким 


// получение кода с СМС отправленного при регистрации
function GetSMSCode: string;

 

Это получается чтение папки Входящие? Что за функция myUnixToDateTime?

 

56 минут назад, Pax Beach сказал:

У в Берлине АПД1 в исходниках, Androidapi.JNI.Provider.pas

есть такой метод.

UPD:

А... обратил внимание, что метод появился с API-19.

В Берлине без апдейта не вижу этого самого Androidapi.JNI.Provider.pas :(

... есть оказывается и getMessagesFromIntent в нем описан :)

Изменено пользователем Rusland
Ссылка на комментарий
  • 0
  • Модераторы
2 минуты назад, Rusland сказал:

Это получается чтение папки Входящие? Что за функция myUnixToDateTime?

да входящие, вот функция

// перевод с UnixDate в TDateTime
function myUnixToDateTime(USec: Longint): TDateTime;
begin
  Result := (USec / 86400) + UnixDateDelta;
end;

 

Ссылка на комментарий
  • 0
51 минуту назад, ZuBy сказал:

const
  SMS_RECEIVED = 'android.provider.Telephony.SMS_RECEIVED';

procedure SMSReceiver(aIntent: JIntent);
//uses
...
end;

не проверял, но по коду так, только как нить вернуть нужно aFrom, aBody

сделать как функцию и тд..

На строчку:

   for I := low(aSmss) to high(aSmss) do

ругается

[DCC Error] Unit1.pas(96): E2008 Incompatible types :( 

 

Ссылка на комментарий
  • 0
  • Модераторы
Только что, Rusland сказал:

На строчку:

   for I := low(aSmss) to high(aSmss) do

ругается

[DCC Error] Unit1.pas(96): E2008 Incompatible types :( 

 

я исправил уже, смотри выше

Ссылка на комментарий
  • 0

Контролировать входящие СМС получается без проблем, спасибо Вашему форму. Подскажите как перехватить свою отправленную СМС, заранее спасибо.

Ссылка на комментарий
  • 1

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

Чтобы реализовывать авторизацию приложения по смс, сам гугл предлагает воспользоваться SmsRetriever, для которого данные разрешения не требуются.

Насколько, я понял, получать интент с СМС можно будет через BroadcastReceiver, реализованный в данной теме, но для этого сначала надо оптравить "заявку" SmsRetriever, чтобы он сгенерил для приложения интент с действием SmsRetriever.SMS_RETRIEVED_ACTION. У гугла для этого приводится код на яве:

// Get an instance of SmsRetrieverClient, used to start listening for a matching
// SMS message.
SmsRetrieverClient client = SmsRetriever.getClient(this /* context */);

// Starts SmsRetriever, which waits for ONE matching SMS message until timeout
// (5 minutes). The matching SMS message will be sent via a Broadcast Intent with
// action SmsRetriever#SMS_RETRIEVED_ACTION.
Task<Void> task = client.startSmsRetriever();

// Listen for success/failure of the start Task. If in a background thread, this
// can be made blocking using Tasks.await(task, [timeout]);
task.addOnSuccessListener(new OnSuccessListener<Void>() {
  @Override
  public void onSuccess(Void aVoid) {
    // Successfully started retriever, expect broadcast intent
    // ...
  }
});

Пожалуйста, помогите, кто шарит, перевести это на дельфи. В библиотеку этот класс не импортирован:(

А еще лучше сразу скомпоновать это вместе с BroadcastReceiver в одном рабочем примере.

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...