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

Pax Beach

Пользователи
  • Постов

    414
  • Зарегистрирован

  • Посещение

  • Победитель дней

    12

Весь контент Pax Beach

  1. Я реализовал пример работы с микрофоном и таймером в Android сервисе, как указано в теме вопроса. Каждый раз, когда вы отправляете в сервис StartCommand или сервис перезапускается, включается запись с микрофона, и сохраняется в каталог с музыков в файл "myrecord.3gp". Надеюсь, мой пример поможет вам создать новые полезные решения. В этом случае пожалуйста делитесь ими с участниками нашего сообщества. Для отладки своих программ на Android используйте запись в LOGI и чтение при помощи monitor.bat (PlatformSDKs\android-sdk-windows\tools). uses ... AndroidApi.JNI.Media, // JMediaRecorder AndroidApi.Timer, // Timer ...; Const TimerInterval = 1000; TimerCounterSecLimit = 10; type TDM = class(TAndroidService) ... private FTimerHandle: Integer; FRecording: Boolean; procedure StartRecord; procedure StopRecord; procedure StartTimer; procedure StopTimer; public FAudioRec: JMediaRecorder; end; procedure Log(const Fmt: string; const Params: array of const); overload; var Msg: string; M: TMarshaller; begin Msg := Format(Fmt, Params); LOGI(M.AsUtf8(Msg).ToPointer); end; procedure Log(const Source: string); overload; var M: TMarshaller; begin LOGI(M.AsUtf8(Source).ToPointer); end; procedure TDM.AndroidServiceCreate(Sender: TObject); begin FTimerHandle := 0; FTimerCounter := 0; FRecording := false; end; procedure TDM.AndroidServiceDestroy(Sender: TObject); begin StopTimer; StopRecord; end; function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; begin if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then begin StopTimer; StopRecord; Result := TJService.JavaClass.START_NOT_STICKY; // don't reload service Log('- service stoped', []); end else begin if not FRecording then begin Log('... sound record to be started', []); StartRecord; StartTimer; end; Result := TJService.JavaClass.START_STICKY; // rerun service if it stops Log('+ Service started', []); end; end; procedure TDM.StartRecord; begin StopRecord; FAudioRec := TJMediaRecorder.Create; FAudioRec.setAudioSource(TJMediaRecorder_AudioSource.JavaClass.MIC); FAudioRec.setOutputFormat(TJMediaRecorder_OutputFormat.JavaClass.THREE_GPP); FAudioRec.setAudioEncoder(TJMediaRecorder_AudioEncoder.JavaClass.AMR_NB); FAudioRec.setOutputFile(StringToJString(TPath.Combine(TPath.GetSharedMusicPath, 'myrecord.3gp'))); try FAudioRec.Prepare(); FAudioRec.start; FRecording := True; Log('+ Start record to %s', [TPath.Combine(TPath.GetSharedMusicPath, 'myrecord.3gp')]); except on E: Exception do Log('- Error in mic recording: %s', [E.Message]); end; end; procedure TDM.StopRecord; begin if Assigned(FAudioRec) then begin if FRecording then begin FRecording := false; try FAudioRec.stop(); FAudioRec.release(); Log('- Mic recording is stoped'); except on E: Exception do Log('- Error in mic stop recording: %s', [E.Message]); end; end; end else begin FRecording := false; end; end; procedure TDM.WaitComplete(TimerId: Integer); begin if FTimerCounter < TimerCounterSecLimit then begin Log('+++ Timer is triggered %d time.', [FTimerCounter]); inc(FTimerCounter); end else StopTimer; end; procedure TDM.StartTimer; begin FTimerCounter := 0; if FTimerHandle = 0 then begin FTimerHandle := AndroidTimerCreate; AndroidTimerSetInterval(FTimerHandle, TimerInterval); end; AndroidTimerSetHandler(WaitComplete); Log('+ Timer started', []); end; procedure TDM.StopTimer; begin if FTimerHandle > 0 then begin Log('... MIC recording to be stopped'); StopRecord; AndroidTimerSetHandler(nil); Log('- Timer stoped', []); end; end; end.
  2. Данная задача реализуется при помощи Android Services и Broadcast Recievers. Искать можно по ключевым словам "android service audio Recorder" здесь, например вот. Пожалуйста поделитесь полученным решением с нашим сообществом. P.S.: здесь я опубликовал свое решения для записи звука микрофона с таймером при запуске сервиса.
  3. Рекомендую посмотреть пример. У товарища все получается, только со временем воспроизведения пакетов какая-то неприятность. Я думаю, из-за того, что он использует TCP, а не конкурентные UDP пакеты (но сам еще не разбирался).
  4. ... Продолжаем исследование по теме: Оказывается в модуле Androidapi.JNI.GraphicsContentViewText есть класс TJSQLiteDatabase, который реализует возможность работы с SQLite на Android. Пример использования этого класса на JAVA я писал выше, осталось просто перенести пример реализацию работы с классом на Delphi.
  5. Товарищи, вот у меня такой же вопрос, как в сабже. Сервис работает в процессе приложения, должен собирать геокоординаты устройства и писать их в базу SQLite, которая используется и самим приложением, в том числе приложение работает с собранными координатами. Подскажите пожалуйста, как обеспечить доступ к SQLite из программы и из сервиса, в какую папку базу лучше деплоить и как к ней из приложения и сервиса обращаться? P.S.: Приложение работает с базой SQLite, но на устройстве базу найти не могу, ни в каталоге приложения, ни поиском по всему устройству. В чем фокус, может кто знает? UPDATE: Я нашел решение, отписался в этой ветке. procedure TDM.conSQLiteBeforeConnect(Sender: TObject); var dbPath: string; begin {$IF DEFINED(iOS) or DEFINED(ANDROID)} dbPath := TPath.Combine(TPath.GetDocumentsPath, 'mybase.sdb'); {$ENDIF} {$IF DEFINED(MSWINDOWS)} dbPath := TPath.GetFullPath(TPath.Combine(TPath.GetLibraryPath, '..\..\..\DataBase\mybase.sdb')); if not FileExists(dbPath) then dbPath := TPath.GetFullPath(TPath.Combine(TPath.GetLibraryPath, 'mybase.sdb')); {$ENDIF} conSQLite.Database := dbPath; end;
  6. Pax Beach

    SQLite и C++

    Не понятно, о какой платформе идет речь. FireDAC в составе RAD Studio имеет встроенный драйвер для SQLite. Может здесь более понятно ответили.
  7. Я нашел решение, к сожалению, пока только для работы с UniDAC: Обновил UniDAC компоненты для Berlin до последней версии (6.3.12). Компоненты TUniConnection и TUniQuery отлично работают с SQLite в Android Service. FireDAC в Android Service пока запустить не удалось, но у меня такой задачи нет. В Deployment host приложения добавляю файл базы данных, Remote Path задаю ".\assets\internal\". И спокойно из сервиса получаю к нему доступ. Мой сервис локальный в одном потоке с приложением. Если делать Intent Service или Remote — наверное, придется помещать файл в другой, доступный каталог, или общаться через намерения (Intents). Надеюсь мой код будет полезен для вас. procedure TDM.conSQLiteBeforeConnect(Sender: TObject); begin {$IF DEFINED(iOS) or DEFINED(ANDROID)} conSQLite.Database := TPath.Combine(TPath.GetDocumentsPath, 'mybase.sqlite'); {$ENDIF} end; procedure TDM.conSQLiteError(Sender: TObject; E: EDAError; var Fail: Boolean); begin Log('--- DB error: %s:', [E.Message]); Fail := False; end; function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; begin Log('+ START with Intent: ' + JStringToString(Intent.getAction.toString), []); if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then begin try conSQLite.Disconnect; Log('- DB disconnected', []); except on E: Exception do Log('- can not to disconnect DB', [E.Message]); end; Log('... service to be stoped', []); JavaService.stopSelf; Result := TJService.JavaClass.START_NOT_STICKY; // don't reload service end else begin Log('... service started', []); try conSQLite.Connect; Log('+ DB connected', []); UniQuery.SQL.Text := 'select count(*) as ALLREC from orders'; UniQuery.Open; if UniQuery.RecordCount > 0 then begin UniQuery.First; Log('... record count: %s', [UniQuery.FieldByName('ALLREC').AsString]); end; UniQuery.Close; except on E: Exception do Log('- can not to connect DB: %s', [E.Message]); end; Result := TJService.JavaClass.START_STICKY; // rerun service if it stops end; end;
  8. Не могу пока вспомнить, где находил примеры многострочных уведомлений (Android однозначно поддерживает это), но я бы переносил строку с символами $0D$0A, а у Вас только \n ($0A). Подробнее об этом здесь.
  9. Спасибо большое! А я-то думаю — Почему у меня вылетает сервис? )
  10. Мне видится, что твой log будет выводиться только в НЕ режиме отладки. Я использую функцию без условий компилятора, и она отлично работает и в приложении, и Android сервисе. procedure Log(const Fmt: string; const Params: array of const); var Msg: string; M: TMarshaller; begin Msg := Format(Fmt, Params); LOGI(M.AsUtf8(Msg).ToPointer); end;
  11. Всегда входящие данные нужно обрабатывать на предмет кодировки, длины и некорректных символов. Дальше, наверняка, Вы будете логи писать в базу или куда-то еще, а уязвимости нам в базе не нужны =) Не понятно, что у Вас за проект, VCL/FMX? Скорее всего, данные приходят в UTF8, а в TMemo пишутся строки ANSI (может наоборот). В любом случае необходимо делать приведение входящих данных к родной кодировке String.
  12. Это не хорошее решение, а костыль, но пока решает мою задачу: Когда нужное мне событие произошло, я просто убираю обработчик таймера AndroidTimerSetHandler(nil); А когда нужен таймер, снова назначаю обработчик: AndroidTimerSetHandler(WaitComplete); При этом таймер продолжает работать и занимать процесс, когда он фактически не нужен — это, конечно же, не хорошо.
  13. В общем, я не хочу, чтобы пользователи обманывали сервер, и подставляли фэйк-координаты. А как определить, что фэйк для моего приложения разрешен в параметрах разработчика, не понимаю.
  14. Brovin Yaroslav, заработал мой сервис с этим модулем, спасибо! Но... при уничтожении таймера умирает сервис и хост, вылетают с ошибкой «Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?)» (ниже в логе). Видимо нужно для таймера сначала нужно как-то выполнить DetachCurrentThread. Не понимаю только — Как? Коллеги, посоветуете, что можно сделать с отключением таймера? Так я запускаю таймер: procedure TDM.StartTimer; begin Log('... timer to be started', []); FTimerHandle := AndroidTimerCreate; FTimerCounter := 0; AndroidTimerSetInterval(FTimerHandle, TimerInterval); AndroidTimerSetHandler(WaitComplete); Log('+ Timer started', []); end; Так я останавливаю таймер. После выполнения таймера, сервис мне не нужен тоже, останавливаю. procedure TDM.StopTimer; begin StopRecord; if FTimerHandle > 0 then begin Log('... timer to be stoped', []); AndroidTimerSetHandler(nil); Log('... timer to be destroyed', []); AndroidTimerDestroy(FTimerHandle); // ТУТ УМИРАЕТ СЕРВИС И ХОСТ-ПРИЛОЖЕНИЕ !!! FTimerHandle := 0; Log('- Timer stoped', []); end; Log('... Service to be stoped', []); JavaService.stopSelf; end; Вот так я работаю с запуском и остановкой сервиса: function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; begin Log('+ START with Intent: ' + JStringToString(Intent.getAction.toString), []); if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then begin StopRecord; StopTimer; Log('...Service to be stoped', []); JavaService.stopSelf; Result := TJService.JavaClass.START_NOT_STICKY; // don't reload service end else begin Log('... service started', []); if not IsMicrophoneRecording then begin Log('... sound record to be started', []); StartRecord; end; StartTimer; Result := TJService.JavaClass.START_STICKY; // rerun service if it stops end; end; А вот лог, где последние две строки — ошибка, при попытке уничтожить таймер: 06-02 16:57:50.460: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libProjectAppRec.so: is missing DT_SONAME will use basename as a replacement: "libProjectAppRec.so" 06-02 16:57:51.679: I/Adreno-EGL(17598): <qeglDrvAPI_eglInitialize:379>: EGL 1.4 QUALCOMM build: Nondeterministic_AU_msm8974_LA.BF.1.1.3__release_AU (I3fa967cfef) 06-02 16:57:51.679: I/Adreno-EGL(17598): OpenGL ES Shader Compiler Version: E031.28.00.02 06-02 16:57:51.679: I/Adreno-EGL(17598): Build Date: 09/29/15 Tue 06-02 16:57:51.679: I/Adreno-EGL(17598): Local Branch: mybranch14683032 06-02 16:57:51.679: I/Adreno-EGL(17598): Remote Branch: quic/master 06-02 16:57:51.679: I/Adreno-EGL(17598): Local Patches: NONE 06-02 16:57:51.679: I/Adreno-EGL(17598): Reconstruct Branch: NOTHING 06-02 16:58:32.859: I/info(17598): FMX: ProjectAppRec: Try to start 06-02 16:58:32.880: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libProxyAndroidService.so: is missing DT_SONAME will use basename as a replacement: "libProxyAndroidService.so" 06-02 16:58:32.905: W/linker(17598): /data/app/com.embarcadero.ProjectAppRec-1/lib/arm/libSvcTimer.so: is missing DT_SONAME will use basename as a replacement: "libSvcTimer.so" 06-02 16:58:33.237: I/info(17598): + START with Intent: StartIntent 06-02 16:58:33.237: I/info(17598): ... service started 06-02 16:58:33.237: I/info(17598): ... timer to be started 06-02 16:58:33.237: I/info(17598): + Timer started 06-02 16:58:34.240: I/info(17598): +++ Timer is triggered time. 06-02 16:58:35.238: I/info(17598): +++ Timer is triggered time. 06-02 16:58:36.238: I/info(17598): +++ Timer is triggered time. 06-02 16:58:37.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:38.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:39.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:40.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:41.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:42.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:43.239: I/info(17598): +++ Timer is triggered time. 06-02 16:58:44.237: I/info(17598): ... timer to be stoped 06-02 16:58:44.237: I/info(17598): ... timer to be destroyed 06-02 16:58:44.237: I/info(17598): - Timer stoped 06-02 16:58:44.237: I/info(17598): ... Service to be stoped 06-02 16:58:44.239: W/art(17598): Native thread exiting without having called DetachCurrentThread (maybe it's going to use a pthread_key_create destructor?): Thread[12,tid=17927,Native,Thread*=0xba7bbb50,peer=0x12d34100,"Thread-42654"] 06-02 16:58:44.239: A/art(17598): art/runtime/thread.cc:1228] Native thread exited without calling DetachCurrentThread: Thread[12,tid=17927,Native,Thread*=0xba7bbb50,peer=0x12d34100,"Thread-42654"]
  15. Вижу, что вопрос надо было задавать по-другому «Как узнать, включены ли фиктивные координаты на Android устройстве и/или для моего приложения?». Сам до сих пор не нашел решения в интернете и коде исходников Delphi.
  16. Сие знает разве что Brovin Yaroslav, в силу опыта. Я на Seattle SDK ставлю последнюю версию, а Platform для совместимости с устройствами беру ниже (14,15, 19) в зависимости от задач проекта.
  17. Спасибо за подсказку. Что-то я не пойму, как с этим модулем правильно работать. Таймер нужно запускать через TTimerManager из FMX.Platform.Android.pas или AndroidTimerCreate->AndroidTimerSetInterval->AndroidTimerSetHandler? Примеров и документации нигде нет. Опять на 4 часа завис с кодом =(
  18. Спасибо, коллеги, за ответы. Начинать холивар, задачи не было. Но вашего опыта вполне достаточно, чтобы обрести свое мнение по поводу выбора фрэймворка.
  19. Попробуйте сделать деплой вручную. Скопировать APK в телефон. Вручную установить APK. Много нюансов при настройке окружения. Я бы проверил и обновил настройки Android SDK, настройки разработчика в телефоне, обновил Java и перенастроил пути. Откомпилировал Release. Залил APK вручную и проверил установку и работу.
  20. dnekrasov, не удалось понять, как правильно запустить таймер в сервисе. Ваш модуль хорошо работает в обычном приложении, но в сервисе не срабатывает событие таймера. Ни WaitComplete, ни TimerThreadOnTimer. Кстати в приведенном Вами примере нет формы, только модуль. Приведу код моего сервиса, и лог, может кто-нибудь поймет в чем дело. Сервис нормально работает и вручную (по событию остановки сервиса) нормально выключается. unit Unit1; interface uses System.SysUtils, System.Classes, System.Android.Service, AndroidApi.JNI.GraphicsContentViewText, AndroidApi.JNI.Os, System.IOUtils, Utils.TimerThread, AndroidApi.JNI.App, // TJService.JavaClass.START_STICKY AndroidApi.Log, AndroidApi.JNI.JavaTypes, // JString AndroidApi.Helpers; // StringToJString const {$IF DEFINED(ANDROID) OR DEFINED(IOS)} AUDIO_FILENAME = 'test.caf'; {$ELSE} AUDIO_FILENAME = 'test.wav'; {$ENDIF} Const RecordTime = 3000; type TDM = class(TAndroidService) function AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; function AndroidServiceHandleMessage(const Sender: TObject; const AMessage: JMessage): Boolean; function AndroidServiceBind(const Sender: TObject; const AnIntent: JIntent): JIBinder; procedure AndroidServiceTaskRemoved(const Sender: TObject; const ARootIntent: JIntent); private FTimer: TTimerThread; procedure WaitComplete(Sender: TObject); procedure TimerThreadStart; procedure TimerThreadStop; procedure TimerThreadOnTimer(Sender: TObject); public { Public declarations } end; var DM: TDM; implementation { %CLASSGROUP 'FMX.Controls.TControl' } {$R *.dfm} procedure Log(const Fmt: string; const Params: array of const); var Msg: string; M: TMarshaller; begin Msg := Format(Fmt, Params); LOGI(M.AsUtf8(Msg).ToPointer); end; function TDM.AndroidServiceBind(const Sender: TObject; const AnIntent: JIntent): JIBinder; begin Log('+ BIND: ' + JStringToString(AnIntent.getAction.toString) + ' - ' + JStringToString(AnIntent.getData.toString), []); end; function TDM.AndroidServiceHandleMessage(const Sender: TObject; const AMessage: JMessage): Boolean; begin Log('+ MESSAGE: ' + JStringToString(AMessage.toString) + ' - ' + JStringToString(AMessage.getData.toString), []); Result := True; end; function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer; begin Log('+ START: ' + JStringToString(Intent.getAction.toString), []); if Intent.getAction.equalsIgnoreCase(StringToJString('StopIntent')) then begin Log('...Service stoped', []); TimerThreadStop; JavaService.stopSelf; Result := TJService.JavaClass.START_NOT_STICKY; end else begin Log('...Service started', []); TimerThreadStart; Result := TJService.JavaClass.START_STICKY; end; end; procedure TDM.AndroidServiceTaskRemoved(const Sender: TObject; const ARootIntent: JIntent); begin Log('- TASK REM: ' + JStringToString(ARootIntent.getAction.toString), []); end; procedure TDM.TimerThreadOnTimer(Sender: TObject); begin TThread.Synchronize(TThread(Sender), procedure begin Log('+++ Timer worked.', []); TimerThreadStop; end); end; procedure TDM.TimerThreadStart; begin if not Assigned(FTimer) then FTimer := TTimerThread.Create(RecordTime, TimerThreadOnTimer) else FTimer.Interval := RecordTime; FTimer.Enabled := True; Log('+ Timer started', []); end; procedure TDM.TimerThreadStop; begin if not Assigned(FTimer) or not FTimer.Enabled then begin Log('- Timer not enabled', []); Exit end else begin FTimer.Enabled := False; FTimer.Cancel; end; Log('- Timer stoped', []); end; procedure TDM.WaitComplete(Sender: TObject); begin TThread.Synchronize(TThread(Sender), procedure var Msg: string; M: TMarshaller; begin Log('- WaitComplete', []); JavaService.stopSelf; Log('Service stoped', []); end); end; end. «FMX: Project1:» — это мой хост, который запускает сервис. Остальное выдает сервис. Что я сделал? — Запустил хост на HTC One, отправил Intent «StartIntent», сервис запустился, поток запустился. Подождал несколько секунд, отправил Intent «StopIntent», поток остановился, сервис остановился. Далее лог: 05-31 23:38:29.709: I/info(17885): FMX: Project1: Try to start 05-31 23:38:29.712: W/linker(17885): /data/app/com.embarcadero.Project1-1/lib/arm/libProject2.so: is missing DT_SONAME will use basename as a replacement: "libProject2.so" 05-31 23:38:29.985: I/info(17885): + START: StartIntent 05-31 23:38:29.985: I/info(17885): ...Service started 05-31 23:38:29.985: I/info(17885): + Timer started 05-31 23:38:41.754: I/info(17885): FMX: Project1: Try to stop 05-31 23:38:41.755: I/info(17885): + START: StopIntent 05-31 23:38:41.755: I/info(17885): ...Service stoped 05-31 23:38:41.755: I/info(17885): - Timer stoped
  21. Pax Beach

    Android MySQL+UniDac+3G

    При распределенной работы с БД, из-за нестабильности канала связи, так и приходится делать. Открыл сокет (при работе с REST), открыл соединение с БД, выполнил запрос, закрыл соединение, если нужно, закрыл сокет. Если выполнение запросов критично по времени, то такое решение — не лучший вариант.
  22. Да? А можно пруфлинк?
  23. Win 10 x64, 12 GB. Проекты вынес на SSD. При работе с проектами, выключаю антивирус, отключаю синхронизацию облака. Берлин компилит проекты под Android быстрее Сиэтла. Под Windows и так моментально. Никаких тормозов в работе часами не увидел.
  24. Pax Beach

    Android MySQL+UniDac+3G

    У меня еще проблема была аналогичная, только MySQL база лежит в Azure. Оказалось, что Yota думает, что я раздаю интернет с коммуникатора на PC — не характерный для смартфона трафик. Т.е. оператор может глушить обращение на не http порты.
  25. Просто мой аргумент за FMX — единообразие палитры параметров компонентов во всех проектах, возможность масштабирования решений (если вдруг понадобится) на другие платформы. У меня есть опасение, что Embrcdr перестанет поддерживать VCL в будущем.
×
×
  • Создать...