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

Евгений Корепов

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

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

  • Посещение

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

    100

Сообщения, опубликованные Евгений Корепов

  1. Добавлю. Есть ТВ-приставки просто с андроидом, а есть приставки с AndroidTV. В первом случае никаких проблем не возникает. Во втором случае - Андрей Ефимов уже подсказал.

    Если решите публиковать приложение для андроид тв, то вот ответ гугля мне:

    Цитата

    Hi Developers at Евгений Корепов,

    Thanks for submitting your app for Android TV. We reviewed Rescan Archer DLNA, with package name com.embarcadero.RescanArcherDLNA, and noticed some eligibility issues.

    App status: Your app has not been accepted into Android TV. This does not affect your app’s status on Google Play.

    Version Name: 1.0.4
    Version code: 4
    Current track: PRODUCTION

    Eligibility issues:

    Your app uses hardware features (such as a touchscreen or camera) that are not available on TV. If your app can operate without the use of those features, you'll need to modify your app's manifest to indicate that your app doesn't require these features. 

    Please consult this list to determine which feature(s) in your app are unsupported:
    http://developer.android.com/training/tv/start/hardware.html#unsupported-features
    ------------
    Your app that does not require a gamepad is inaccessible with a 5-way DPAD.

    Please refer to our Enable D-pad Navigation documentation for details:
    http://developer.android.com/training/tv/start/navigation.html#d-pad-navigation
    ------------
    Your app or game has vertical letterboxing when displayed on TVs. Please design your TV app or game to be displayed in landscape mode.

    Please refer to our Basic TV Layouts documentation for more details:
    http://developer.android.com/training/tv/start/layouts.html#structure
    ------------
    Your app does not contain a full-size app banner.

    We are targeting 1080P, which we consider xhdpi. Apps should include the banner in the xhdpi (320 dpi) drawables folder with a size of (320px × 180px).

    Please refer to our Home Screen Banner and UI Patterns documentation:
    http://developer.android.com/training/tv/start/start.html#banner
    http://developer.android.com/design/tv/patterns.html#banner
    ------------

     

  2.  '%3D' вместо "=", '%26' вместо "&" - это стандартная методика отправки данных, называется url encoding. Принимающая сторона производит обратный процесс url decoding. Это нормальный процесс экранирования спецсимволов для HTTP протокола.

    Если вы не хотите использовать encoding (хотя в ContentType := 'application/x-www-form-urlencoded' вы утверждаете обратное), по используйте опцию poDoNotEncode в опциях параметра (http://docwiki.embarcadero.com/Libraries/Tokyo/en/REST.Types.TRESTRequestParameterOption)

    А лучше используйте штатный THTTPClient (System.Net.HTTPClient) и будет счастье, потому как TRest, рожденный в недрах безумия Эмбы, преподнесет вам не мало неприятных сюрпризов.

  3. Если вы хотите передавать данные не тронутыми, то используйте бинарную передачу. 

    А вообще \t это общепринятый символ экранирования табуляции (символа с кодом 9) - вы его как раз и передаете. Можете на выходе делать замену \n на chr(9)

  4. 4 часа назад, Александр Лукьянов (Filament Extruder 1.75) сказал:

    Active возвращается в true

    Если tcpserver1 на форму бросить и не создавать компонент то работает пример. Использую 10.1  berlin. Дома попробую 10.1 update 2 и отпишусь

    Ну значит точно в биндинге дело, делайте вот так:

    FIdTCPServer.Bindings.Clear;
      with FIdTCPServer.Bindings.Add do
      begin
        IP:='0.0.0.0';
        Port:=ATCPPort;    
      end;
      

     

  5. Попробуйте проверять запустился ли сервер. К примеру так

    function TForm.CreateTCPServer(Var ATCPPort : Word) : Boolean;
    begin
      Result:=False;
      FIdTCPServer:=TIdTCPServer.Create;
      FIdTCPServer.OnExecute:=IdTCPServerExecute;
      FIdTCPServer.OnConnect:=IdTCPServerConnect;
      FIdTCPServer.Bindings.Clear;
      with FIdTCPServer.Bindings.Add do
      begin
        IP:='0.0.0.0';
        Port:=ATCPPort;    
      end;
      try
        FIdTCPServer.Active:=True;
      except
        Result:=False;
      end;
      if FIdTCPServer.Active then
        Result:=True;
    end;

     

  6. 29 минут назад, Alex7wrt сказал:

    Спасибо за совет, но это смахивает на костыль ))

    И в каком событии проверять координаты?

    Кстати, есть ли способ узнать координаты курсора мыши в пределах всей формы с учетом дочерних компонентов?

    Проверяйте не в событии, а в таймере. Координаты в глобальной переменной   Screen.MousePos

  7. 23 минуты назад, Alex7wrt сказал:

    Добрый день

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

    Есть заполненный ListBox, в общем случае он с вертикальной полосой прокрутки. Нужно отработать для него событие OnMouseLeave. Точнее говоря, нужно сделать так, чтобы, когда курсор мыши покидает его пределы, то делаем ListBox.Visible:=False.

    Но дело в том, что, во-первых, событие OnMouseLeave в общем случае не сработает, так как в заполненном ListBox-е это событие перехватывается его дочерними элементами, а во вторых вертикальная полоса прокрутки не учитывается в событиях мыши ListBox-а. К тому же, даже если я сделаю какую-нибудь внешнюю рамку, и буду обрабатывать OnMouseLeave для нее, то все равно при быстром движении мыши событие не происходит.

    Потому прошу подсказать оптимальный и надежный способ, как сделать ListBox невидимым, когда курсор мыши находится за его пределами.

    Наверно этот вопрос относится к любому компоненту, который полностью заполнен по площади дочерними компонентами, и в комплекте со скролбоксом.

     

     

    Таймер, эдак милисекунд на 200. И в нем проверять координаты мыши, дальше уже арифметика.

  8. 6 часов назад, Alex7wrt сказал:

    ИМХО, можно разместить версию с Target SDK 14, чтобы собрать как можно большую аудиторию, а в ноябре обновить приложение до 26.

    Аудитория никак не изменится. Ведь minSdkVersion="14" все равно перекроет и все старые устройства.

  9. 3 минуты назад, Barvin сказал:

    При сабмите приложения на play market получаю такое сообщение. Подскажите что и где нужно поменять в опциях проекта? Спасибо.

    Требования к целевому уровню API с августа 2018 г.

    Предупреждение

    Целевой уровень API для вашего приложения – 14. Чтобы обеспечить необходимую производительность и безопасность, целевой уровень API должен быть не ниже 26.

    С августа 2018 года целевая версия новых приложений должна быть не менее Android 8.0 (API уровня 26).
    С ноября 2018 года целевая версия обновляемых приложений должна быть не менее Android 8.0 (API уровня 26).

    Совет

    Измените целевой уровень API для приложения.

     

    Файл AndroidManifest.template.xml в папке проекта (это шаблон манифеста). 

    Строка:

        <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="%targetSdkVersion%" />

    Если вы откомпилируете проект и посмотрите подготовленный средой файл манифеста в папке

    \Android\Debug\AndroidManifest.xml

    то увидите что приложение предназначено для API десятилетней давности:

        <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />

    Гугль от вас просит примерно такого

        <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="26" />

    Измените шаблон AndroidManifest.template.xml:

        <uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="26" />

     

  10. Принцип работы такой:

    1. На форму кладете стандартный TImage
    2. Создаете экземпляр TGifPlayer
    3. Задаете свойство FGifPlayer.Image:=Image; где Image это лежащая на форме TImage
    4. Загружаете гифку FGifPlayer.LoadFromFile('D:\Embarcadero\Projects\ShareCode\FMX.GifUtils\GIF_Example.gif');
    5. Запускаем проигрывание гифки FGifPlayer.Play;

    Вот код:

    unit UnitFormMain;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
      FMX.GifUtils, FMX.Objects;
    
    type
      TFormMain = class(TForm)
        Image: TImage;
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        FGifPlayer : TGifPlayer;
      end;
    
    var
      FormMain: TFormMain;
    
    implementation
    
    {$R *.fmx}
    
    procedure TFormMain.FormCreate(Sender: TObject);
    begin
      FGifPlayer:=TGifPlayer.Create(Self);
      FGifPlayer.Image:=Image;
      FGifPlayer.LoadFromFile('D:\Embarcadero\Projects\ShareCode\FMX.GifUtils\GIF_Example.gif');
      FGifPlayer.Play;
    end;
    
    end.

    Вот демо проект во вложении:

     

    FMX.GifUtils.Demo.zip

  11. Проверьте, с помощью Tcpview.exe к примеру, какое приложение слушает на этом порту.

    P.S. Зарапортовался... порт же не известен. Только некий адрес.

  12. 15 минут назад, Вадим Шавров сказал:

    Вроде бы ошибок нет, а как правильно её вызывать?

    ShareFile('storage/emulated/0/Documents/data.db', ???);

    aFileName - это полный путь к файлу или только название "data.db'?

    aComment - а это я вообще не понимаю для чего :(

    Конечно полный путь, откуда иначе функция узнает какой файл отправлять?

    AComment - комментарий, типа "Это файл №199 из такого то приложения, служит для того то". Но никто не заставляет вас его писать.

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

    И убедитесь в существовании файла перед его отправкой.

  13. Вот так:

    procedure ShareFile(aFileName, aComment : String);
    {$IFDEF ANDROID}
    var Intent    : JIntent;
        uri       : Jnet_Uri;
        AttachmentFile: JFile;
        S : String;
    {$ENDIF ANDROID}
    begin
    {$IFDEF ANDROID}
      Intent := TJIntent.Create;
      Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
      Intent.setFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);
      S:=TPath.GetFileName(aFileName);
      Intent.putExtra(TJIntent.JavaClass.EXTRA_SUBJECT, StringToJString(aComment));
      Intent.putExtra(TJIntent.JavaClass.EXTRA_TEXT, StringToJString(aComment));
      AttachmentFile := TJFile.JavaClass.init(StringToJString(aFileName));
      Uri := TJnet_Uri.JavaClass.fromFile(AttachmentFile);
      Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, TJParcelable.Wrap((Uri as ILocalObject).GetObjectID));
      Intent.setType(StringToJString('text/plain'));
    //  Intent.setDataAndType(StrToJURI('file:' + TPath.Combine(TPath.GetSharedDownloadsPath, 'picture.png')), StringToJString ('image/png'));
      SharedActivity.startActivity(Intent);
    {$ENDIF ANDROID}
    end;

    Из давнего проекта, возможно нужно будет подправить согласно сообщениям компилятора.

    P.S. И файл должен находится в общедоступной папке (к примеру TPath.GetSharedDocumentsPath)

  14. 10 минут назад, hippocamus сказал:

    Добавляю ран-тайм в TVertScrollBox компоненты TLabel со свойством Align = Top

    В первый раз всё в порядке, а после очистки контейнера и заливки новых TLabel порядок изменён:
    если сначала было 1 2 3 4 5 6 7 8, то начиная со второго раза лейблы идут в таком порядке: 1 8 7 6 5 4 3 2.
    Причём в TVertScrollBox.Content.Children они содержатся в правильном порядке. Не важно, добавляю ли я их через AddObject или через InsertObject.
    Та же беда с добавлением чекбоксов в TExplorer.

    Решал удалением родительского компонента и созданием его заново каждый раз. Но хочется нормального решения.

    Кроме Align = Top, установите еще свойство Top = 10000, что бы они добавлялись заведомо ниже всех существующий элементов. Если не устанавливать Top, то по умолчанию позиция у всех будет 0 и порядок элементов будет зависеть от погрешности вычислений Single.

  15. Накидал вам пример с использование потокобезопасной очереди.

    Логика работы такая - поток получает данные и заталкивает их в очередь, в главной форме, в таймере (можно изменить на OnIdle), проверяем очередь на наличие данных, если данные есть, то обрабатываем их.

    Обратите внимание на процедуру создания очереди - FQueue:=TThreadedQueue<TRec_Data>.Create(100, 1000, 10); где первый параметр размер очереди (100), второй таймаут заталкивания данных в очередь (1000мс = 1сек), третьй таймаут вытаскивания из очереди (10 мс - маленький,  чтоб таймер у нас не подвисал в ожидании).

    Вот код проекта целиком

    unit Unit1;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
      System.Generics.Collections,
      IdTCPClient, IdGlobal, FMX.Controls.Presentation, FMX.ScrollBox, FMX.Memo;
    
    type
      TRec_Data = record
        Flag: array[0..20] of char;
      end;
    
      TMyThread = class(TThread)
      private
        Progress: string;
        Client : TIdTCPClient;
        FQueue : TThreadedQueue<TRec_Data>;
      protected
        procedure Execute; override;
      public
        constructor Create(const AQueue : TThreadedQueue<TRec_Data>);
        destructor Destroy; override;
      end;
    
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        { Private declarations }
        FQueue : TThreadedQueue<TRec_Data>;
        FMyThread : TMyThread;
        Timer : TTimer;
        procedure OnTimer(Sender: TObject);
      public
        Memo1: TMemo;
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      FQueue:=TThreadedQueue<TRec_Data>.Create(100, 1000, 10);
    
      Timer:=TTimer.Create(Self);
      Timer.Interval:=100;
      Timer.OnTimer:=OnTimer;
      Timer.Enabled:=True;
    
      FMyThread:=TMyThread.Create(FQueue);
      FMyThread.Start;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      if Assigned(FMyThread) then
      begin
        FMyThread.Terminate;
        FMyThread.WaitFor;
        FMyThread.Free
      end;
      if Assigned(Timer) then
        Timer.Free;
      if Assigned(FQueue) then
        FQueue.Free;
    end;
    
    procedure TForm1.OnTimer(Sender: TObject);
    Var ARec : TRec_Data;
    begin
    //  while FQueue.PopItem(ARec) = TWaitResult.wrSignaled do или
      if FQueue.PopItem(ARec) = TWaitResult.wrSignaled then
        Form1.Memo1.Lines.Insert(0, ARec.Flag);
    end;
    
    constructor TMyThread.Create(const AQueue : TThreadedQueue<TRec_Data>);
    var
      Rec: TRec_Data;
      Buffer: TIdBytes;
    begin
      inherited Create(true);
    
      FQueue:=AQueue;
    
      Client := TIdTCPClient.Create(nil);
      Client.Host := '127.0.0.1';
      Client.Port := 6000;
      Client.Connect;
    
      // Передаем данные
      if Client.Connected = True then
      begin
        Rec.Flag := 'addUser';
    
        Buffer := RawToBytes(Rec, SizeOf(Rec));
        Client.IOHandler.Write(Buffer);
      end;
    end;
    
    destructor TMyThread.Destroy;
    begin
      if Assigned(Client) then
        Client.Free;
      inherited;
    end;
    
    procedure TMyThread.Execute;
    var
      Rec: TRec_Data;
      Buffer: TIdBytes;
    begin
      while Not Terminated do
      begin
        if Client.Connected then
        begin
          Client.IOHandler.ReadBytes(Buffer, SizeOf(Rec));
          BytesToRaw(Buffer, Rec, SizeOf(Rec));
          Progress := Rec.Flag;
    //      Synchronize(SetProgress);
          FQueue.PushItem(Rec);
        end
        else
          Client.Connect;
        TThread.Sleep(10);
      end;
    end;
    
    
    end.

    Так же обратите внимание, я переписал ваш метод Execute на правильный. В вашей реализации, поток завершался при потере соединения.

  16. Тут в процессе работы над одним проектом понадобилось узнать IP адрес устройства. Очень не хотелось включать дополнительные разрешения приложению. Думал ограничится одним "Доступ в Интернет".

    Вот как это можно сделать:

    С помощью TIdUDPServer посылаем широковещательное сообщение, с помощью того же TIdUDPServer сами получаем его и в ABinding узнаем с какого IP оно пришло. Таким образом мы узнаем IP адрес интерфейса с маршрутом по умолчанию.

    Вот код, все просто:

    unit Unit1;
    
    interface
    
    uses
      System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
      FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
      IdGlobal, IdSocketHandle, IdBaseComponent, IdComponent, IdUDPBase, IdUDPServer;
    
    const ConstUDPSendString = 'dfgb2hd3f6gbf';
    
    type
      TForm1 = class(TForm)
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
        FUDPServer : TIdUDPServer;
        FMyIP : String;
        procedure OnUDPServerUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle);
        procedure GetMyIP;
      public
        { Public declarations }
    
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.fmx}
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      GetMyIP;
    end;
    
    procedure TForm1.GetMyIP;
    begin
      FMyIP:='';
      FUDPServer:=TIdUDPServer.Create;
      FUDPServer.DefaultPort:=46734;
      FUDPServer.BroadcastEnabled:=True;
      FUDPServer.OnUDPRead:=OnUDPServerUDPRead;
      FUDPServer.Active:=True;
      FUDPServer.Broadcast(ConstUDPSendString, FUDPServer.DefaultPort);
    end;
    
    procedure TForm1.OnUDPServerUDPRead(AThread: TIdUDPListenerThread; const AData: TIdBytes; ABinding: TIdSocketHandle);
    begin
      AThread.Synchronize(AThread,
        procedure
        begin
          if BytesToString(AData).Equals(ConstUDPSendString) and FMyIP.IsEmpty then
            FMyIP:=ABinding.PeerIP;
        end
      );
    end;
    
    end.

     

  17. В 20.03.2018 в 11:07, kiz35196 сказал:

    как включитьт её если у меня компонент не на форме,а создается через THTTPClient.create; ?

     

    Var FIAsyncResult : IAsyncResult;
    	procedure DoEndDownload(const ASyncResult: IAsyncResult);
    ....
      FHTTPClient:=THTTPClient.Create;
      FHTTPClient.ResponseTimeout:=FResponseTimeout;
      FHTTPClient.ConnectionTimeout:=FConnectionTimeout;
    
      FIAsyncResult:=FHTTPClient.BeginGet(DoEndDownload, 'https://.....');
    
    procedure TServiceData.DoEndDownload(const ASyncResult: IAsyncResult);
    Var LAsyncHTTPResponse : IHTTPResponse;
    begin
      try
        LAsyncHTTPResponse:=THTTPClient.EndAsyncHTTP(AsyncResult);
      except
        on E:Exception do
          AErrorMessage:=E.Message;
      end;
      if Assigned(LAsyncHTTPResponse) then
      begin
        if LAsyncHTTPResponse.StatusCode = 200 then
          S:=LAsyncHTTPResponse.ContentAsString;
    ....
    

     

  18. 4 часа назад, YurMak сказал:

    Здравствуйте, Евгений! Еще раз спасибо!

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

    Не могли бы вы прокомментировать (расписать)  Ваш пример.  Новичку многое непонятно. Как заполняется  ListView (и почему именно ListView, а не ListBox);

    как изменить Стиль Item`а (мне, например, не нужно "чекить" его);

    как заносятся данные в ItemDetail (мне, например, нужно выводить не дату создания файла, а ID Tag Mp3 файда, не размер его, а длительность в мин.сек (после обработки в соответсвенной процедуре);

    в примере есть процедура:

    procedure TFormMain.SpeedButton1Click(Sender: TObject);
    Var ASelected : TArray<TFileManagerSelectedItem>;
    begin
      ASelected:=FileManager.GetSelected;
    end; 

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

    Разъясните новичку. Думаю, и другим будет интересно.

    С уважением!

    Listview против ListBox - листбокс тут не подходит, он проще, но гораздо тяжеловеснее. В некоторые папки вы зайти не сможете, при использовании Листбокса. К примеру C:\Windows\System32, у меня там 4 тысячи файлов, и даже под винду для листбокса это будет тяжело. В TFileManager использование ListView не самое оптимальное (лучше использовать DynamicApperance), но на тот момент мне нужен был работающий менеджер и за пол часа

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

     

    Процедуру SpeedButton1Click(Sender: TObject) игнорируйте, просто класс TFileManager я писал для какого то своего проекта, и этот пример, в некоторых местах, просто куски бессмысленного кода...

     

  19. 3 часа назад, YurMak сказал:

    Огромное спасибо!!!

    Весьма необычное (для меня) решение, но, главное, работает и решает мою проблему.

    Спасибо.

    P.S. Можно еще обращаться с вопросами напрямую? (если дадите "координаты" (E-mail, Skype, Viber, Messenger...) куда писать).

    Нет, напрямую не надо, форум - лучший способ, мало ли кому пригодится.

×
×
  • Создать...