Jump to content

vfbb

Members
  • Content Count

    266
  • Joined

  • Last visited

  • Days Won

    30

Posts posted by vfbb


  1. @Eric Bonilha

    I know you! You are Eric of Digifort. I am Vinícius, Paulo's brother. We started programming at the same time, about 15 years ago, at the time of intertruco, lncb, ydm. 😂😂😂. I send you my contact in private.

     

    Eric, this tutorial that I posted is just to detect which url opened your app, to parsing the URL. This is useful just when you have many Universal Links or wildcard in the Universal Link.

     

    I made today a tutorial explain how to configure the Universal Link, You can see here: https://github.com/viniciusfbb/fmx_tutorials/blob/master/delphi_ios_universal_links/README.md


  2. About Tasks there are infinite ways to do this, try to create a bitmap for each Task, you create from outside and destroy from outside the Task. You will cancel the current Task, and without WaitFor it, create a new one with a new bitmap, when closing the form or the application you check the TPair <TBitmap, TTask> list using Task.WaitFor and then Bitmap.Free.

     

    About the performance, I will disappoint you a little. The delphi TBitmap is meant to be used only on the main thread. Although it works in threads, it has a critical section that does not allow you to paint TBitmap and process Paint forms simultaneously. But, as I said, although it doesn't take full advantage of the computer's potential, the program will work, and it probably won't harm you.


  3. 4 hours ago, Rollo62 said:

    @vfbb

    Why do you think this is not threadsafe, because of the

    
    AIPV6.Replace(

    or because of the use of BigInteger at all ?

     

    When using local string copies of the first case, this should be OK,
    or are the BigInteger intrinsically not threadsafe (never used them yet) ?

    Because the used methods of the BigInteger changes a global variable.

    BigInteger.Hex;
    BigInteger.Decimal;

    But I was looking, it is possible to avoid these methods, so this new example will be safe:

    uses
      System.SysUtils, Velthuis.BigIntegers;
    
    function IPV6ToNumberDigits(const AIPV6: string): string;
    var
      LBigInteger: BigInteger;
    begin
      if not BigInteger.TryParse(AIPV6.Replace(':', '', [rfReplaceAll]), 16, LBigInteger) then
        Exit('');
      Result := LBigInteger.ToString;
    end;
    
    function NumberDigitsToIPV6(const ANumberDigits: string): string;
    var
      I: Integer;
    begin
      if ANumberDigits.IsEmpty then
        Exit('');
      Result := BigInteger(ANumberDigits).ToHexString.ToLower.PadLeft(32, '0');
      for I := 0 to 6 do
        Result := Result.Insert((4 * (I+1)) + I, ':');
    end;

     

    • Like 1

  4. uses
      System.SysUtils, VCL.Dialogs, Velthuis.BigIntegers;
    
    function IPV6ToNumberDigits(const AIPV6: string): string;
    var
      LBigInteger: BigInteger;
    begin
      BigInteger.Hex;
      LBigInteger := AIPV6.Replace(':', '', [rfReplaceAll]);
      BigInteger.Decimal;
      Result := string(LBigInteger);
    end;
    
    function NumberDigitsToIPV6(const ANumberDigits: string): string;
    var
      LBigInteger: BigInteger;
      I: Integer;
    begin
      LBigInteger := ANumberDigits;
      BigInteger.Hex;
      Result := string(LBigInteger).ToLower.PadLeft(32, '0');
      BigInteger.Decimal;
      for I := 0 to 6 do
        Result := Result.Insert((4 * (I+1)) + I, ':');
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    const
      IPV6_EXAMPLE = '2001:0db8:85a3:08d3:1319:8a2e:0370:7344';
    begin
      showmessage('Original: ' + IPV6_EXAMPLE + #13#10 +
        'Number digits: ' + IPV6ToNumberDigits(IPV6_EXAMPLE) + #13#10 +
        'IPV6 of the number digits: ' + NumberDigitsToIPV6(IPV6ToNumberDigits(IPV6_EXAMPLE)));
    end;

    Result:

    Original: 2001:0db8:85a3:08d3:1319:8a2e:0370:7344
    Number digits: 42540766452641195744311209248773141316
    IPV6 of the number digits: 2001:0db8:85a3:08d3:1319:8a2e:0370:7344

    Remarks: This solution is not thread-safe.


  5. I suggest you to create 2 different packages, with the same units, but need to have 2 different packages, one for FMX and one for VCL. After create packages, add in the project options of each package, in the "Conditional defines" something like USING_VCL for the vcl package and USING_FMX for the fmx package.In code of your .pas just use:

    {$IFDEF USING_VCL}
    ...
    {$ENDIF}

    or

    {$IFDEF USING_FMX}
    ...
    {$ENDIF}

     

    • Like 1

  6. Depois que as suas tabelas estiverem exatamente da forma como te falei, você poderá usar o seguinte código:

     

    var
      LCountriesIds: TArray<Integer>;
      LCitiesIds: TArray<Integer>;
      
    // Load the cities after country combobox change
    procedure TForm1.ComboBox1Change(Sender: TObject);
    begin
      ComboBox2.Items.Clear;
      SetLength(LCitiesIds, 0);
      if ComboBox1.ItemIndex = -1 then
        Exit;
      FDQuery1.SQL.Text := 'SELECT city.city_id, city.city_name FROM city WHERE city.country_id=:country_id ORDER BY city.city_name ASC;';
      FDQuery1.ParamByName('country_id').AsInteger := LCountriesIds[ComboBox1.ItemIndex];
      FDQuery1.Open;
      try
        while not FDQuery1.Eof do
        begin
          LCitiesIds := LCitiesIds + [FDQuery1.FieldByName('city_id').AsInteger];
          ComboBox2.Items.Add(FDQuery1.FieldByName('city_name').AsString);
          FDQuery1.Next;
        end;
      finally
        FDQuery1.Close;
      end;
    end;
    
    // Load all countries after form show
    procedure TForm1.FormShow(Sender: TObject);
    begin
      ComboBox1.Items.Clear;
      SetLength(LCountriesIds, 0);
    
      FDQuery1.SQL.Text := 'SELECT country.country_id, country.country_name FROM country ORDER BY country.country_name ASC;';
      FDQuery1.Open;
      try
        while not FDQuery1.Eof do
        begin
          LCountriesIds := LCountriesIds + [FDQuery1.FieldByName('country_id').AsInteger];
          ComboBox1.Items.Add(FDQuery1.FieldByName('country_name').AsString);
          FDQuery1.Next;
        end;
      finally
        FDQuery1.Close;
      end;
      ComboBox1Change(nil);
    end;

    Entender este simples código é primordial para fazer qualquer query.


  7. You can not change an signed iOS, MacOS or Android app, if the app is not yours. But if you have the source, the app is your, you have many options to save the resources strings, you can save in particular files for it. When we are talking about resources strings for translation, this will allow the store to make smallest packages of your app, because it will not delivery the unnecessary data to the user. For example, a French language will not be included in a user that use English language ... However, this languages files isn’t usually big, and I personally prefer to make a cross platform solution, languages in the source code. Other solution is storing it in a SQLite.

    • Like 1

  8. You need 2 tables with this fields

     

    city
       city_id
       country_id
       city_name

     

    country
       country_id
       country_name

     

    Get all countries
    SELECT * FROM country ORDER BY country.country_name ASC;

     

    Get all cities with their countries
    SELECT * FROM city LEFT JOIN country ON city.country_id=country.country_id ORDER BY city.city_name ASC;

     

    Get all cities of a specific country (change the XXX to the country number)
    SELECT * FROM city WHERE city.country_id=XXX ORDER BY city.city_name ASC;


  9. 16 hours ago, Jacek Laskowski said:

    No, I don't need runtime. But I didn't find a web tool. If you know one, please give me a link.

    Just search in the web "remove unused css" you will find many tools.

    Recently, I used the Google Chrome without any extension, just open the DevTools > Sources > Coverage.

    You will see a report with all unused css codes and javascripts.

    ss.png


  10. Compile with Android 64 with AAB Bundles enabled and selected the Application Store configuration. But before it, check the Libraries (jar files) of the Android 64 bits, some jar file may be missing.
    Note: when you compile the AAB, to make the Android 32 and Android 64 packages, the delphi will only consider the jar files from Android64 libraries... I had the same problem, because my Android 32 had the google vision jars but after made the AAP Bundles, in the Android 32 bits the jar files weren’t there.

    • Thanks 1

  11. 4 hours ago, Lars Fosdal said:

    What about the theory that dictionaries have fewer collisions if you set the capacity to be a prime number ?

    It depends on the hash algorithm being used but in general it will not make a difference.

    4 hours ago, Lars Fosdal said:

    Fact or fiction? Personally, I do actually set the capacity to a prime, but I am not quite sure if it because it is sound advice or if it is a cargo cult thing. 

    For the dictionary implemented in delphi, the best capacity is always twice the number of items for known collections and 0 for unknown collections.

     


  12. 12 hours ago, David Heffernan said:

    Ofc, you don't have to use the RTL dictionary. The spring4d dictionary has a number of powerful features. 

     

    I don't think the growth is such an issue. Collisions can be, and better hash and proving would help. The issue for small collections is simply the constant of proportionality. O(1) can be slower than O(log n) for small enough n. 

    It is not an issue, it is the cost benefit of higher performance x less memory. But the optimization of creating it with the capacity is valid considering that the performance increases is on average 30%, both for small collections and for large ones.

    uses
      System.SysUtils, System.Generics.Collections, System.Diagnostics, FMX.Dialogs;
    
    procedure TestWithInitialCapacity;
    var
      I, J, LNewId: Integer;
      LDictionary: TDictionary<Integer, Boolean>;
      LStopwatch: TStopwatch;
    begin
      LStopwatch := TStopwatch.StartNew;
      for I := 0 to 10000 do begin
        LDictionary := TDictionary<Integer, Boolean>.Create(200);
        for J := 0 to 100 do begin
          repeat
            LNewId := Random(High(Integer));
          until not LDictionary.ContainsKey(LNewId);
          LDictionary.Add(LNewId, True);
        end;
        FreeAndNil(LDictionary);
      end;
      showmessage(Format('Test with initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds]));
    end;
    
    procedure TestWithoutInitialCapacity;
    var
      I, J, LNewId: Integer;
      LDictionary: TDictionary<Integer, Boolean>;
      LStopwatch: TStopwatch;
    begin
      LStopwatch := TStopwatch.StartNew;
      for I := 0 to 10000 do begin
        LDictionary := TDictionary<Integer, Boolean>.Create;
        for J := 0 to 100 do begin
          repeat
            LNewId := Random(High(Integer));
          until not LDictionary.ContainsKey(LNewId);
          LDictionary.Add(LNewId, True);
        end;
        FreeAndNil(LDictionary);
      end;
      showmessage(Format('Test without initial capacity: %g', [LStopwatch.Elapsed.TotalMilliseconds]));
    end;

     

     

    • Like 1
×