Евгений Корепов
-
Постов
738 -
Зарегистрирован
-
Посещение
-
Победитель дней
100
Активность репутации
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
в исходном сообщении - ссылка, про "андроид"...
так что не только fmx
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
это здорово, что у тебя (тут на "ты" принято?) работает )
но я не адвокат FMX, и проблема - с FMX, один из способов решения - такой
хотя, в принципе, можно попробовать и по другому... но, собсна, это не моя проблема, мне просто было интересно её решить )
-
Евгений Корепов получил реакцию от Ingalime в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
Полностью готовое и верное решение тут https://gist.github.com/ashumkin/3e2e213d657162ae26d364a85c64b472
Главное правильно в батнике выставить все пути.
P.S. Давно бы уже эмба создала систему перекомпиляции прямо в среде, чтоб не мучаться с батниками в винде.
-
Евгений Корепов получил реакцию от Barbanel в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
Где моё блюдечко!!! И чтоб с голубой каёмочкой!!! ? Просто никогда с этим не связывался, да и времени погружаться в нюансы просто нет (
Связал. Мой косяк. Дебильный путь от Эмбы сбил с толку. Вот первый раз решил SDK решил оставить по умолчанию (обычно он у меня в D:\PlatformSDKs) и теперь пожинаю плоды...
Теперь гораздо-гораздо лучше! Куча предупреждений, но кто их читает ?
D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>D:\Embarcadero\fmx.jar-rebuild.cmd D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>set JAVA_HOME=C:\Program Files\Java\jre1.8.0_121 D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>set SDK=C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>if "xD:\Embarcadero\Studio\20.0" == "x" set BDS=D:\Embarcadero\Studio\20.0 D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>cd D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>PATH C:\Program Files\Java\jre1.8.0_121\bin\;C:\Program Files\Java\jre1.8.0_121\bin\;C:\Program Files\Java\jre1.8.0_121\bin\;C:\Program Files\Java\jre1.8.0_121\bin\;C:\Program Files\Java\jre1.8.0_121\bin\;d:\Embarcadero\Studio\20.0\bin;d:\Embarcadero\Studio\20.0\bin64;D:\Embarcadero\ Studio\20.0\bin;C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl;D:\Embarcadero\Studio\20.0\bin64;C:\Users\Public\Documents\Embarcadero\Studio\20.0\Bpl\Win64;C:\Program Files\Microsoft MPI\Bin\;C:\ProgramData\Oracle\Java\javapath;D:\PlatformSDKs\Windows Kits\10\Windows Performance Toolkit\;C:\Program Files\Java\jdk1.7.0_71\bin;C:\WINDOWS\s ystem32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\OpenSSL-Win32\bin\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\OpenVPN\bin;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\PHP\;C:\ProgramData\ ComposerSetup\bin;C:\ProgramData\ComposerSetup\bin;C:\Users\ekore\AppData\Local\Microsoft\WindowsApps;C:\Users\ekore\AppData\Local\GitHubDesktop\bin;C:\Users\ekore\AppData\Local\Microsoft\WindowsApps;C:\PHP\;C:\Users\ekore\AppData\Roaming\Composer\vendor\bin;C:\ProgramData\ComposerSetup\bin; & javac com\embarcadero\firemonkey\webbrowser\* .java -cp "C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar" warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/graphics/Bitmap.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/net/http/SslError.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/os/Message.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/KeyEvent.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/HttpAuthHandler.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/SslErrorHandler.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/WebView.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/content/Context.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/WebChromeClient.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/GeolocationPermissions.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/GeolocationPermissions$Callback.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewTreeObserver.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewTreeObserver$OnGlobalFocusChangeListener.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewGroup.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewGroup$OnHierarchyChangeListener.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewParent.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewManager.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/View.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/graphics/drawable/Drawable.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/graphics/drawable/Drawable$Callback.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/KeyEvent$Callback.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/os/Parcelable.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/InputEvent.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/accessibility/AccessibilityEventSource.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/widget/AbsoluteLayout.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/webkit/WebViewClient.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewDebug.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewDebug$ExportedProperty.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewDebug$IntToString.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewDebug$FlagToString.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/view/ViewGroupOverlay.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. warning: C:\Users\Public\Documents\Embarcadero\Studio\20.0\CatalogRepository\AndroidSDK-2525_20.0.33219.4899\platforms\android-26\android.jar(android/util/SparseArray.class): major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. Note: com\embarcadero\firemonkey\webbrowser\WebClient.java uses or overrides a deprecated API. Note: Recompile with -Xlint:deprecation for details. 32 warnings D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>rem copy "D:\Embarcadero\Studio\20.0\lib\android\debug\fmx.jar" "D:\Embarcadero\Studio\20.0\lib\android\debug\fmx.jar.bak" D:\Embarcadero\Studio\20.0\source\rtl\androiddex\java\fmx\src>jar -vuf "D:\Embarcadero\Studio\20.0\lib\android\debug\fmx.jar" com\embarcadero\firemonkey\webbrowser\OnWebViewListener.class com\embarcadero\firemonkey\webbrowser\WebBrowser.class com\embarcadero\firemonkey\webbrowser\WebBrowser$1.class com\embarcadero\firemonkey\webbrowser\WebClien t.class adding: com/embarcadero/firemonkey/webbrowser/OnWebViewListener.class(in = 1183) (out= 482)(deflated 59%) adding: com/embarcadero/firemonkey/webbrowser/WebBrowser.class(in = 1023) (out= 508)(deflated 50%) adding: com/embarcadero/firemonkey/webbrowser/WebClient.class(in = 2881) (out= 1151)(deflated 60%) adding: com/embarcadero/firemonkey/webbrowser/WebBrowser$1.class(in = 861) (out= 454)(deflated 47%) И в конце приз - все заработало! Геолокация в браузере работает. Браузер даже не спросил разрешения на геолокацию - в приложении разрешения есть, и как я понимаю теперь браузер использует разрешения приложения.
-
Евгений Корепов получил реакцию от Barbanel в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
Бедные программисты java, сколько странных и не очевидных нюансов (((
А для release достаточно поменять пути? Никаких дополнительных опций не нужно?
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
major version 52 is newer than 51, the highest major version supported by this compiler. It is recommended that the compiler be upgraded. это, вроде, убирается by
javac -source 1.7 -target 1.7 ...
похоже, либы в android.jar скомпилены Java 7, а тут Java 8
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
читал её текст?
а коли приводишь лог вызова, весь приводи
но подозреваю, что тебе надо задать set BDS=
в правильное значение (у меня 32-битная ОС, у тебя наверняка 64-битная, и путь к Делфи отличается от твоего)
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
ну тут опечатался
всё вам на блюдечке надо...
свяжи воедино
с ошибками вида
com\embarcadero\firemonkey\webbrowser\OnWebViewListener.java:3: error: package android.graphics does not exist
-
Евгений Корепов отреагировална Alexey Shumkin в Андроид, WebBrowser, Geolocation - запрос разрешения на геолокацию
Расписывать в деталях прям не буду
https://gist.github.com/ashumkin/3e2e213d657162ae26d364a85c64b472
-
Евгений Корепов отреагировална Евгений (KeeperWorld) в Отображение картинок в ListView
Да там всё Евгений Корепов сделал уже. Я только три копейки своих добавил...
Вот конечный код:
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Net.HttpClient, System.Generics.Collections, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListView; const ListViewItemImageEmpty = -1; ListViewItemImageLoading = 0; ListViewItemImageLoaded = 1; type TForm1 = class(TForm) ListView1: TListView; Layout1: TLayout; Button1: TButton; procedure Button1Click(Sender: TObject); procedure ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FListViewUpdating : Boolean; FHTTPClient : THTTPClient; FAsyncResultList : TList<IAsyncResult>; procedure LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); procedure ClearListViewAndCancelAsynchronousRequests(); public end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin listview1.ItemIndex := 0; listview1.ItemAppearance.ItemAppearance := 'Custom'; listview1.ItemAppearanceObjects.ItemObjects.Accessory.Visible := False; FHTTPClient := THTTPClient.Create; FAsyncResultList := TList<IAsyncResult>.Create; FListViewUpdating := False; end; procedure TForm1.FormDestroy(Sender: TObject); begin ClearListViewAndCancelAsynchronousRequests(); FListViewUpdating := True; FreeAndNil(FAsyncResultList); if Assigned(FHTTPClient) then FHTTPClient.Free; end; procedure TForm1.ClearListViewAndCancelAsynchronousRequests(); var I: Integer; begin FListViewUpdating := True; // Запрещаем продолжать загружать фотки (если ещё не успели загрузиться все) while FAsyncResultList.Count > 0 do // Дожидаемся окончания выполнения всех IAsyncResult.Cancel, несмотря на асинхронность begin for I := FAsyncResultList.Count - 1 downto 0 do if Assigned(FAsyncResultList.Items) and (not FAsyncResultList.Items.IsCompleted) then FAsyncResultList.Items.Cancel else FAsyncResultList.Delete(I); // Заодно удаляем отработанные элементы end; ListView1.Items.Clear; FListViewUpdating := False; end; procedure TForm1.Button1Click(Sender: TObject); var I: Integer; Item: TListViewItem; ARandom: Integer; begin ClearListViewAndCancelAsynchronousRequests(); //Формирование нового списка for I := 1 to 10000 do begin FAsyncResultList.Add(nil); FListViewUpdating := True; Item := listview1.Items.Add; Item.Height := 45; Randomize; ARandom := Random(6); case ARandom of 0 : Item.data['ImageURL'] := 'http://fire-monkey.ru/uploads/monthly_2017_06/me.thumb.jpg.966ddc17d5602ee14feb43479c1f6963.jpg'; 1 : Item.data['ImageURL'] := 'http://fire-monkey.ru/uploads/monthly_2018_05/B-IpGQmVgTM.thumb.jpg.2ebeb0bd766ab7cf19f10195d6ea2be9.jpg'; 2 : Item.data['ImageURL'] := 'http://fire-monkey.ru/uploads/monthly_2016_04/10.png.b9ab371e8fd38172fee96bcf75fb6699.thumb.png.b0685259b03bfff540903913845532a5.png'; 3 : Item.data['ImageURL'] := 'https://secure.gravatar.com/avatar/9942c50b1641a921c52d4b389bd718d6?d=http://fire-monkey.ru/uploads/monthly_2017_12/K_member_87.png'; 4 : Item.data['ImageURL'] := 'http://fire-monkey.ru/uploads/monthly_2016_11/photo-1529.png.7267be10b59f950b7c5bb3f34a60901e.thumb.png.22027ae85266216220310ed694d57628.png'; 5 : Item.data['ImageURL'] := 'http://fire-monkey.ru/uploads/profile/photo-thumb-115.jpg'; end; Item.Data['ImageState'] := ListViewItemImageEmpty; FListViewUpdating := False; Item.Adapter.ResetView(Item); end; end; procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); var K: Integer; // Анонимная процедура захватывает локальную переменную, а не обращается к AItem, которой уже может не быть в момент _окончания_ скачивания фотки AAsyncResult: IAsyncResult; begin if Not Assigned(AItem) or Not Assigned(AListItemImage) then Exit; if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpty then Exit; if AItem.Data['ImageURL'].AsString.IsEmpty then Exit; AItem.Data['ImageState'] := ListViewItemImageLoading; K := AItem.Index; // Запоминаем индекс в локальную K, которая уйдёт в анонимку (время жизни K > времени жизни анонимки) FAsyncResultList.Items[K] := FHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) var AHTTPResponse: IHTTPResponse; begin if ASyncResult.IsCancelled then Exit; try AHTTPResponse := THTTPClient.EndAsyncHTTP(ASyncResult); if Not Assigned(AHTTPResponse) then Exit; if AHTTPResponse.StatusCode <> 200 then Exit; except Exit; end; TThread.Synchronize(Nil, procedure begin if FListViewUpdating or ASyncResult.IsCancelled then // Выходим, так как внутри анонимной процедуры AItem или AListItemImage - не сброшены в nil, хотя их уже может и не быть Exit; if Not Assigned(AItem) or Not Assigned(AListItemImage) then Exit; AListItemImage.BeginUpdate; AListItemImage.Bitmap := TBitmap.Create; AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream); AListItemImage.EndUpdate; AItem.Data['ImageState'] := ListViewItemImageLoaded; FAsyncResultList.Items[K] := nil; end ); end, AItem.Data['ImageURL'].AsString ); end; procedure TForm1.ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); function SetupImageObject(const AName: String; AWidth, AHeight, X , Y: Single; AAlign, AVertAlign: TListItemAlign): TListItemImage; var LIT: TListItemText; begin Result := TListItemImage(AItem.View.FindDrawable(AName)); if Result = Nil then begin // Создаём картинку Result := TListItemImage.Create(AItem); Result.Name := AName; Result.Bitmap := nil; Result.OwnsBitmap := True; // Создаём надпись LIT := TListItemText.Create(AItem); LIT.Name := 'LIT-' + AItem.Index.ToString; LIT.Width := 100; LIT.Height := 22; LIT.PlaceOffset.X := X + AWidth + 10; LIT.PlaceOffset.Y := Y; LIT.Text := LIT.Name; LIT.Visible := True; end; Result.Width := AWidth; Result.Height := AHeight; Result.PlaceOffset.X := X; Result.PlaceOffset.Y := Y; Result.Align := AAlign; Result.VertAlign := AVertAlign; Result.ScalingMode := TImageScalingMode.StretchWithAspect; Result.Visible := True; end; Var AListItemImage: TListItemImage; begin if FListViewUpdating then Exit; AListItemImage := SetupImageObject('s_image', 35, 35, 0 , 0, TListitemalign.Leading, TListItemAlign.Center); LoadImage(AItem, AListItemImage); AHandled := True; end; end.
-
Евгений Корепов отреагировална Евгений (KeeperWorld) в Отображение картинок в ListView
Евгений, спасибо громадное за код!!! Красиво и лаконично!
Но под Rio всё равно крэшится с ошибкой, если приложение закрывать раньше, чем успевают загрузиться фотки:
Project Test21.exe raised exception class ENetHTTPClientException with message 'Error receiving data: (12017) Операция отменена'.
Поправил вот так:
procedure TForm1.ClearListViewAndCancelAsynchronousRequests();
var
I: Integer;
begin
FListViewUpdating := True; // Запрещаем продолжать загружать фотки (если ещё не успели загрузиться все)
while FAsyncResultList.Count > 0 do // Дожидаемся окончания выполнения всех IAsyncResult.Cancel, несмотря на асинхронность
begin
for I := FAsyncResultList.Count - 1 downto 0 do
if Assigned(FAsyncResultList.Items) and (not FAsyncResultList.Items.IsCompleted) then
FAsyncResultList.Items.Cancel
else
FAsyncResultList.Delete(I); // Заодно удаляем элементы (раннее не удалялись - утечка памяти)
end;
ListView1.Items.Clear;
FListViewUpdating := False;
end;
=====
Тоже, кстати, пару раз поймал ошибку в TMonitor и в TDictionary.
Выяснил, что возникает из-за обращения к элементам списка в LoadImage, когда их уже нет. Пофиксил так (отмечено синим):
procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage);
var
K: Integer; // Анонимная процедура захватывает локальную переменную, а не обращается к AItem, которой уже может не быть в момент _окончания_ скачивания фотки
AAsyncResult: IAsyncResult;
begin
if Not Assigned(AItem) or Not Assigned(AListItemImage) then
Exit;
if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpty then
Exit;
if AItem.Data['ImageURL'].AsString.IsEmpty then
Exit;
AItem.Data['ImageState'] := ListViewItemImageLoading;
K := AItem.Index; // Запоминаем индекс в локальную K, которая уйдёт в анонимку (время жизни K > времени жизни анонимки)
FAsyncResultList.Items[K] := FHTTPClient.BeginGet(
procedure (const ASyncResult: IAsyncResult)
var
AHTTPResponse: IHTTPResponse;
begin
if ASyncResult.IsCancelled then
Exit;
try
AHTTPResponse := THTTPClient.EndAsyncHTTP(ASyncResult);
if Not Assigned(AHTTPResponse) then
Exit;
if AHTTPResponse.StatusCode <> 200 then
Exit;
except
Exit;
end;
TThread.Synchronize(Nil,
procedure
begin
if FListViewUpdating then // Выходим, так как внутри анонимной процедуры AItem или AListItemImage - не сброшены в nil, хотя их уже может и не быть
Exit; // Кстати, наверное, правильнее было бы вместо проверки FListViewUpdating использовать и/или условие: if ASyncResult.IsCancelled then Exit; ?
if Not Assigned(AItem) or Not Assigned(AListItemImage) then
Exit;
AListItemImage.BeginUpdate;
AListItemImage.Bitmap := TBitmap.Create;
AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream);
AListItemImage.EndUpdate;
AItem.Data['ImageState'] := ListViewItemImageLoaded;
FAsyncResultList.Items[K] := nil; // Наверное, это присвоение лучше вытащить наверх, перед проверкой всех условий? Ведь фотка скачалась успешно... Или не надо?
end
);
end,
AItem.Data['ImageURL'].AsString
);
end;
====
Прогонял много раз, клацал по кнопке один и много раз и закрывал сразу приложение, ошибки пока больше не появлялись... Тьфу-тьфу-тьфу....
-
Евгений Корепов отреагировална IVGSoft в обновление визуальных компонентов
Снабдите запрос уникальным идентификатором.
-
Евгений Корепов получил реакцию от Maximus в обновление визуальных компонентов
Вот тут лучше перестраховаться и взять за правило принцип "Любое обращение к адресному пространству другого потока выполнять потокобезопасными способами". Потому как даже чтение может привести к непредсказуемым результатам - читаете вы данные, строку к примеру из другого потока, прочитали половину, а тот поток в это время перезаписал содержимое ячеек памяти, и вы после этого читаете оставшуюся половину. Вместо ожидаемых данных получаете черте что. Это грубый пример конечно.
-
Евгений Корепов получил реакцию от Ingalime в Подключение Html файла через {$R name.RES name.rc} Android и iOS
Не понял, что значит выводит "одной строкой"? Причем тут Memo? И где танец с бубном? )))
Если вы хотите загружать файл напрямую с помощью Navigate, то делайте это:
Делаем ресурс
В деплоймент вы сразу увидите этот файл (он никуда не встраивается, а кладется как есть)
И вам остается только лишь сделать
WebBrowser1.Navigate('file://путь_до_вашего_файла'); Вы это хотели получить?
-
Евгений Корепов получил реакцию от Ingalime в Подключение Html файла через {$R name.RES name.rc} Android и iOS
Вы можете на этапе создания формы загрузить из ресурсов нужный вам HTML:
procedure TFormMain.InitResource(); var ResStream: TResourceStream; SL : TStringList; begin ResStream := TResourceStream.Create(hInstance, 'HTMLSpinner', RT_RCDATA); SL:=TStringList.Create; SL.LoadFromStream(ResStream, TEncoding.UTF8); FHTMLSpinner:=SL.Text; ResStream.Free; SL.Free; end; Где FHTMLSpinner, типа String, и из этой строки уже грузить в браузер по мере надобности:
FWB.LoadFromStrings(FHTMLSpinner, 'localhost');
-
Евгений Корепов получил реакцию от Alex Bozhko в PUSH и Android8
Вроде все работает. Разрешений не нужно никаких вообще. До этого ошибочно требовало доступ к учетной записи - пофиксили.
Код вроде остался прежним, вот кусок из живого проекта:
procedure TFormMain.InitPushService; begin TTask.Run( procedure Var ADeviceID, ADeviceToken : String; begin FPushService := TPushServiceManager.Instance.GetServiceByName(TPushService.TServiceNames.GCM); FPushService.AppProps[TPushService.TAppPropNames.GCMAppID] := ConstGCMAppID; if Assigned(FPushService) then begin FPushServiceConnection := TPushServiceConnection.Create(FPushService); FPushServiceConnection.OnChange := OnPushServiceConnectionChange; FPushServiceConnection.OnReceiveNotification := OnReceivePushNotificationEvent; FPushServiceConnection.Active := True; ADeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; ADeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; FSettings.Flags.PushServiceInited:=True; TThread.Synchronize(TThread.CurrentThread, procedure () begin FNetwork.DeviceID:=ADeviceID; FNetwork.DeviceToken:=ADeviceToken; FNetwork.SendFCMRegistration(); // ?????????????????????????????? Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); end); end; end ); end; procedure TFormMain.OnPushServiceConnectionChange(Sender: TObject; AChange: TPushService.TChanges); begin TThread.Synchronize(TThread.CurrentThread, procedure () begin if TPushService.TChange.DeviceToken in AChange then begin if Assigned(FNetwork) then begin FNetwork.DeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; FNetwork.DeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); FNetwork.SendFCMRegistration(); end; end; end ); end; procedure TFormMain.OnReceivePushNotificationEvent(Sender: TObject; const ANotification: TPushServiceNotification); var AMessageSection : String; begin if Assigned(ANotification.Json) then begin Log('Push Message Json'); Log(ANotification.Json.ToString); if ANotification.Json.TryGetValue('message_section', AMessageSection) then begin if AMessageSection.Equals('support') then SetActiveTab(ConstSectionSupport); if AMessageSection.Equals('news') then SetActiveTab(ConstSectionNews); if AMessageSection.Equals('info') then SetActiveTab(ConstSectionInfo); if AMessageSection.Equals('services') then SetActiveTab(ConstSectionServices); end else if FSettings.CurrentSection <> ConstSectionSupport then SetActiveTab(ConstSectionSupport) else WebBrowserCallJS(TCallJS.SupportLoadContent); PlaySoundEffects(1); end; end; procedure TFormMain.CheckStartupNotifications(); var CurNotification : TPushServiceNotification; begin if Length(FPushService.StartupNotifications) > 0 then for CurNotification in FPushService.StartupNotifications do if Assigned(CurNotification) then OnReceivePushNotificationEvent(Self, CurNotification); NotificationCenter.CancelAll; end; Метод FNetwork.SendFCMRegistration() отсылает регистрацию на мой сервер. В методе проверяется получение регистрации и факт отсылки (он может вызываться несколько раз у меня)
-
Евгений Корепов получил реакцию от Евгений (KeeperWorld) в Отображение картинок в ListView
У меня на Rio работает как то не стабильно. Изредка вылазят исключения при закрытии приложения. То в TMonitor, то даже в TDictionary (((
Сделал стабильную версию - работает быстро и без глюков, но с использованием внешнего списка с IAsyncResult. Добавил процедуру ClearListViewAndCancelAsynchronousRequests() где выполняется Cancel и очищается ListView. Теперь можно клацать по кнопке сколько угодно.
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.ListView.Types, FMX.ListView.Appearances, FMX.ListView.Adapters.Base, System.Net.HttpClient, System.Generics.Collections, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Layouts, FMX.ListView; const ListViewItemImageEmpy = -1; ListViewItemImageLoading = 0; ListViewItemImageLoaded = 1; type TForm1 = class(TForm) ListView1: TListView; Layout1: TLayout; Button1: TButton; procedure Button1Click(Sender: TObject); procedure ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } FListViewUpdating : Boolean; FHTTPClient : THTTPClient; FAsyncResultList : TList<IAsyncResult>; procedure LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); procedure ClearListViewAndCancelAsynchronousRequests(); public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.FormCreate(Sender: TObject); begin listview1.ItemIndex:=0; listview1.ItemAppearance.ItemAppearance:='Custom'; listview1.ItemAppearanceObjects.ItemObjects.Accessory.Visible:=false; FHTTPClient:=THTTPClient.Create; FAsyncResultList:=TList<IAsyncResult>.Create; FListViewUpdating:=False; end; procedure TForm1.FormDestroy(Sender: TObject); begin ClearListViewAndCancelAsynchronousRequests(); FListViewUpdating:=True; if Assigned(FHTTPClient) then FHTTPClient.Free; end; procedure TForm1.ClearListViewAndCancelAsynchronousRequests(); Var I : Integer; begin for I := 0 to FAsyncResultList.Count - 1 do if FAsyncResultList.Items[I] <> Nil then if Not FAsyncResultList.Items[I].IsCompleted then FAsyncResultList.Items[I].Cancel; FListViewUpdating:=True; for I := ListView1.Items.Count - 1 downto 0 do ListView1.Items.Delete(I); FListViewUpdating:=False; end; procedure TForm1.Button1Click(Sender: TObject); var i:integer; item:TListViewItem; ARandom : Integer; begin ClearListViewAndCancelAsynchronousRequests(); //Формирование нового списка for i := 1 to 10000 do begin FAsyncResultList.Add(Nil); FListViewUpdating:=True; Item:=listview1.Items.Add; item.Height:=45; Randomize; ARandom:=Random(6); case ARandom of 0 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2017_06/me.thumb.jpg.966ddc17d5602ee14feb43479c1f6963.jpg'; 1 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2018_05/B-IpGQmVgTM.thumb.jpg.2ebeb0bd766ab7cf19f10195d6ea2be9.jpg'; 2 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_04/10.png.b9ab371e8fd38172fee96bcf75fb6699.thumb.png.b0685259b03bfff540903913845532a5.png'; 3 : item.data['ImageURL']:='https://secure.gravatar.com/avatar/9942c50b1641a921c52d4b389bd718d6?d=http://fire-monkey.ru/uploads/monthly_2017_12/K_member_87.png'; 4 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/monthly_2016_11/photo-1529.png.7267be10b59f950b7c5bb3f34a60901e.thumb.png.22027ae85266216220310ed694d57628.png'; 5 : item.data['ImageURL']:='http://fire-monkey.ru/uploads/profile/photo-thumb-115.jpg'; end; Item.Data['ImageState']:=ListViewItemImageEmpy; FListViewUpdating:=False; item.Adapter.ResetView(item); end; end; procedure TForm1.LoadImage(const AItem: TListViewItem; const AListItemImage : TListItemImage); Var AAsyncResult : IAsyncResult; begin if Not Assigned(AItem) or Not Assigned(AListItemImage) then exit; if AItem.Data['ImageState'].AsInteger <> ListViewItemImageEmpy then exit; if AItem.Data['ImageURL'].AsString.IsEmpty then exit; AItem.Data['ImageState']:=ListViewItemImageLoading; FAsyncResultList.Items[AItem.Index]:=FHTTPClient.BeginGet( // AItem.TagObject:=TBaseAsyncResult(FHTTPClient.BeginGet( // FHTTPClient.BeginGet( procedure (const ASyncResult: IAsyncResult) Var AHTTPResponse : IHTTPResponse; begin if ASyncResult.IsCancelled then begin exit; end; try AHTTPResponse:=THTTPClient.EndAsyncHTTP(ASyncResult); if Not Assigned(AHTTPResponse) then exit; if AHTTPResponse.StatusCode <> 200 then exit; except exit; end; TThread.Synchronize(Nil, procedure begin if Not Assigned(AItem) then exit; if Not Assigned(AListItemImage) then exit; AListItemImage.BeginUpdate; AListItemImage.Bitmap:=TBitmap.Create; AListItemImage.Bitmap.LoadFromStream(AHTTPResponse.ContentStream); AListItemImage.EndUpdate; AItem.Data['ImageState']:=ListViewItemImageLoaded; FAsyncResultList.Items[AItem.Index]:=Nil; end ); end, AItem.Data['ImageURL'].AsString ); end; procedure TForm1.ListView1UpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); function SetupImageObject(const AName : String; AWidth, AHeight, X , Y : Single; AAlign, AVertAlign: TListItemAlign) : TListItemImage; begin Result:=TListItemImage(AItem.View.FindDrawable(AName)); if Result = Nil then begin Result:=TListItemImage.Create(AItem); Result.Name:=AName; Result.Bitmap:=Nil; Result.OwnsBitmap:=True; end; Result.Width:=AWidth; Result.Height:=AHeight; Result.PlaceOffset.X:=X; Result.PlaceOffset.Y:=Y; Result.Align:=AAlign; Result.VertAlign:=AVertAlign; Result.ScalingMode:=TImageScalingMode.StretchWithAspect; Result.Visible:=True; end; Var AListItemImage : TListItemImage; begin if FListViewUpdating then exit; AListItemImage:=SetupImageObject('s_image', 35, 35, 0 , 0, TListitemalign.Leading, TListItemAlign.Center); LoadImage(AItem, AListItemImage); AHandled:=true; end; end.
-
Евгений Корепов отреагировална Vitaldj в СУБД SQLite не открывается в Run Time
Скорее всего что то с путем напутали. Попробуйте перед коннектом, проверить путь к файлу БД. Есть ли файл? Может он у вас вообще не деплоится?
-
Евгений Корепов отреагировална krapotkin в Фреймворк для стандартного ListView
Всем привет!
После долгого творческого запоя я запилил альфу либы, которая позволяет юзать обычный, не-генномодифицированный ListView.
Данные подаются в виде модели данных, описание раскладки итема лежит в JSON.
Для работы пишется примитивный наследник класса-адаптера, который биндит данные модели на элементы из JSON. Я решил, что руками делать это дешевле чем через RTTI
{ TMyAdapter } procedure TMyAdapter.SetupDrawableContent(const ADrawable: TListItemDrawable; const AData: TMyData); begin if SameText( ADrawable.Name, 'text') then begin (ADrawable as TListItemText).Text := AData.Text; end else if SameText( ADrawable.Name, 'detail') then begin (ADrawable as TListItemText).Text := AData.Detail; end else if SameText( ADrawable.Name, 'balance') then begin (ADrawable as TListItemText).Text := FormatFloat('0.00', AData.Balance); end else if SameText( ADrawable.Name, 'reserved') then begin (ADrawable as TListItemText).Text := FormatFloat('0.00', AData.Reserved); end end; Главная хитрость и отличие от стандартного DynamicAppearance+LiveBinding - переменная высота итемов ListView и использование арифметики в описании раскладки.
Можно указать Detail.Y = Text.Bottom + 5, ItemHeight = Detail.Bottom + 10, Detail.W = ItemWidth/2 - X
{"Kind":"rect","Name":"BonusRect","Value":"", "Place":{"X":"itemwidth*3/4","Y":"Text.Y","W":"ItemWidth/4-5","H":"50"}, "BorderColor":"#FF005500", "Color":"lime", "LineWidth":3}, {"TextHAlign":2,"TextVAlign":1,"Kind":"text","Name":"Balance","Value":"", "Place":{"X":"BonusRect.x+5","Y":"Text.Y","W":"BonusRect.w-10","H":"50"}, "WordWrap":true, "Color" :"Black", "Font":{"Size":18,"Style":""}} ], "ItemHeight":"detail.bottom+10", Это дает довольно гибкую систему. Не на все случаи жизни, но все, что нужно, можно после автоматической раскладки дополнительно приписать в OnUpdateObjects
Код для работы примерно такой
procedure TForm1.FormCreate(Sender: TObject); begin data:= TMyDataList.CreateFromFile(ExePath()+'data.json'); // загрузка в модель данных Adapter := TMyAdapter.Create(lvWallets, data.Items, ExePath()+'pattern.json'); // создание адаптера и загрузка шаблона Adapter.Pattern.SetupListView(lvWallets); // задать отступы и разную мелочь Adapter.ResetView(); // здесь в цикле из модели данных создается нужное количество итемов ListView end; procedure TForm1.lvWalletsUpdatingObjects(const Sender: TObject; const AItem: TListViewItem; var AHandled: Boolean); begin Adapter.SetupContent(AItem); // загрузить текст, картинки и другое содержимое в элементы итема Adapter.Pattern.DoLayout(AItem); // поправить раскладку в соответствии с содержимым элементов итема end; Для использования кроме стандартных - текст, картинка, кнопка - создано несколько дополнительных элементов итема - прямоугольник, круг, уголок, линия.
Отличие от ModernLV - ,
1) все происходит без правки системных файлов
2) пока нет колонок и других особых изысков.
На результат работы смотреть тут. Исходники пока не причесаны, будут чуть позже
UPD.
ссылки на репозитории ниже
-
Евгений Корепов получил реакцию от Yarpda в Поток в Android-е
С помощью стандартного (тот что в палитре компонентов Delphi) это сделать затруднительно. Вам нужно написать свои реализации классов TMedia и TCustomMediaCodec. Это долгий и муторный путь. Но можно забыть про куцую оболочку Эмбаркадеро под названием TMediaPlayer, и все становиться гораздо проще:
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, Androidapi.JNI.Media, Androidapi.Helpers, Androidapi.JNI.GraphicsContentViewText, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } FPlayer : JMediaPlayer; public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin FPlayer:=TJMediaPlayer.Create; FPlayer.setDataSource(StringToJString('http://cdndl.zaycev.net/228184/8640962/lana_del_rey_-_hope_is_a_dangerous_thing_for_a_woman_like_me_to_have_but_i_have_it_%28zaycev.net%29.mp3')); FPlayer.prepare; FPlayer.Start(); end; end.
-
Евгений Корепов получил реакцию от Dev в Runtime permissions in Delphi 10.3 Rio
Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;
-
Евгений Корепов получил реакцию от Ingalime в PUSH и Android8
Вроде все работает. Разрешений не нужно никаких вообще. До этого ошибочно требовало доступ к учетной записи - пофиксили.
Код вроде остался прежним, вот кусок из живого проекта:
procedure TFormMain.InitPushService; begin TTask.Run( procedure Var ADeviceID, ADeviceToken : String; begin FPushService := TPushServiceManager.Instance.GetServiceByName(TPushService.TServiceNames.GCM); FPushService.AppProps[TPushService.TAppPropNames.GCMAppID] := ConstGCMAppID; if Assigned(FPushService) then begin FPushServiceConnection := TPushServiceConnection.Create(FPushService); FPushServiceConnection.OnChange := OnPushServiceConnectionChange; FPushServiceConnection.OnReceiveNotification := OnReceivePushNotificationEvent; FPushServiceConnection.Active := True; ADeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; ADeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; FSettings.Flags.PushServiceInited:=True; TThread.Synchronize(TThread.CurrentThread, procedure () begin FNetwork.DeviceID:=ADeviceID; FNetwork.DeviceToken:=ADeviceToken; FNetwork.SendFCMRegistration(); // ?????????????????????????????? Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); end); end; end ); end; procedure TFormMain.OnPushServiceConnectionChange(Sender: TObject; AChange: TPushService.TChanges); begin TThread.Synchronize(TThread.CurrentThread, procedure () begin if TPushService.TChange.DeviceToken in AChange then begin if Assigned(FNetwork) then begin FNetwork.DeviceID := FPushService.DeviceIDValue[TPushService.TDeviceIDNames.DeviceID]; FNetwork.DeviceToken := FPushService.DeviceTokenValue[TPushService.TDeviceTokenNames.DeviceToken]; Log('DeviceID: ' + FNetwork.DeviceID); Log('DeviceToken: ' + FNetwork.DeviceToken); FNetwork.SendFCMRegistration(); end; end; end ); end; procedure TFormMain.OnReceivePushNotificationEvent(Sender: TObject; const ANotification: TPushServiceNotification); var AMessageSection : String; begin if Assigned(ANotification.Json) then begin Log('Push Message Json'); Log(ANotification.Json.ToString); if ANotification.Json.TryGetValue('message_section', AMessageSection) then begin if AMessageSection.Equals('support') then SetActiveTab(ConstSectionSupport); if AMessageSection.Equals('news') then SetActiveTab(ConstSectionNews); if AMessageSection.Equals('info') then SetActiveTab(ConstSectionInfo); if AMessageSection.Equals('services') then SetActiveTab(ConstSectionServices); end else if FSettings.CurrentSection <> ConstSectionSupport then SetActiveTab(ConstSectionSupport) else WebBrowserCallJS(TCallJS.SupportLoadContent); PlaySoundEffects(1); end; end; procedure TFormMain.CheckStartupNotifications(); var CurNotification : TPushServiceNotification; begin if Length(FPushService.StartupNotifications) > 0 then for CurNotification in FPushService.StartupNotifications do if Assigned(CurNotification) then OnReceivePushNotificationEvent(Self, CurNotification); NotificationCenter.CancelAll; end; Метод FNetwork.SendFCMRegistration() отсылает регистрацию на мой сервер. В методе проверяется получение регистрации и факт отсылки (он может вызываться несколько раз у меня)
-
Евгений Корепов получил реакцию от Brovin Yaroslav в Runtime permissions in Delphi 10.3 Rio
Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;
-
Евгений Корепов отреагировална Barbanel в Runtime permissions in Delphi 10.3 Rio
В поставке 10.3 есть примеры работы с правами.
Один из них можно найти по такому пути:
C:\Users\Public\Documents\Embarcadero\Studio\20.0\Samples\Object Pascal\Multi-Device Samples\Media\PhotoEditorDemo
Что интересно, этот же код можно без изменений использовать под любой другой платформой.
На других платформах приложение будет считать что ему выдали все права, и соответственно запускать все коллбеки без каких-либо вопросов.
-
Евгений Корепов получил реакцию от Barbanel в Runtime permissions in Delphi 10.3 Rio
Для полноценной работы вам нужно добавить параметры в вызов (иначе вы не узнаете дал ли пользователь разрешение или нет)
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], nil); Вот так:
PermissionsService.RequestPermissions([FPermissionWrite, FPermissionRead], PermissionRequestResult, ExplainReason); PermissionRequestResult - это обработка ответа пользователя
procedure TForm.PermissionRequestResult(Sender: TObject; const APermissions: TArray<string>; const AGrantResults: TArray<TPermissionStatus>); begin if (Length(AGrantResults) = 2) and (AGrantResults[0] = TPermissionStatus.Granted) and (AGrantResults[1] = TPermissionStatus.Granted) then begin // Ура! Пользователь дал разрешение на оба наших запроса. Выставялем глобальные флаги (к примеру) которые сигнализируют что можно читать/писать карту памти end else TDialogService.ShowMessage('Не возможно продолжить работу, требуемые разрешения не получены') end; И ExplainReason - если пользователь сдуру не дал разрешение, то вам нужно объяснить ему что без этого приложение работать не будет.
procedure TForm.ExplainReason(Sender: TObject; const APermissions: TArray<string>; const APostRationaleProc: TProc); begin TDialogService.ShowMessage('Приложению нужен доступ к карте памяти для таких то целей, иначе приложение не сможет работать. Зайдите в настроки Андроид и дайте разрешение на доступ', procedure(const AResult: TModalResult) begin APostRationaleProc; end) end;